Version 0.14

This commit is contained in:
spaceflint 2021-08-04 22:10:27 +03:00
parent 3d84879ed8
commit 8d9552a154
46 changed files with 2090 additions and 605 deletions

View File

@ -46,6 +46,7 @@ System.IWellKnownStringEqualityComparer
System.DivideByZeroException System.DivideByZeroException
System.FormatException System.FormatException
System.FormattableString
System.Func`* System.Func`*
System.ICloneable System.ICloneable
@ -89,6 +90,7 @@ System.Collections.ObjectModel.ReadOnlyCollection`1
System.Collections.HashHelpers System.Collections.HashHelpers
System.Collections.Generic.Comparer`1 System.Collections.Generic.Comparer`1
System.Collections.Generic.IComparer`1
System.Collections.Generic.ComparisonComparer`1 System.Collections.Generic.ComparisonComparer`1
System.Collections.Generic.ObjectComparer`1 System.Collections.Generic.ObjectComparer`1
System.Collections.Generic.GenericComparer`1 System.Collections.Generic.GenericComparer`1
@ -189,13 +191,18 @@ System.Reflection.CallingConventions
System.Reflection.IntrospectionExtensions System.Reflection.IntrospectionExtensions
System.Reflection.IReflect System.Reflection.IReflect
System.Reflection.MemberInfo System.Reflection.MemberInfo
System.Reflection.FieldAttributes
System.Reflection.FieldInfo System.Reflection.FieldInfo
System.Reflection.EventAttributes
System.Reflection.EventInfo System.Reflection.EventInfo
System.Reflection.ParameterAttributes System.Reflection.ParameterAttributes
System.Reflection.ParameterInfo System.Reflection.ParameterInfo
System.Reflection.PropertyAttributes
System.Reflection.PropertyInfo System.Reflection.PropertyInfo
System.Reflection.MethodAttributes
System.Reflection.MethodInfo System.Reflection.MethodInfo
System.Reflection.ConstructorInfo System.Reflection.ConstructorInfo
System.Reflection.TypeAttributes
System.Reflection.TypeInfo System.Reflection.TypeInfo
System.Reflection.MemberFilter System.Reflection.MemberFilter
System.Reflection.Missing System.Reflection.Missing
@ -204,7 +211,6 @@ System.Reflection.InterfaceMapping
System.Reflection.InvalidFilterCriteriaException System.Reflection.InvalidFilterCriteriaException
System.Reflection.IReflectableType System.Reflection.IReflectableType
System.Reflection.ParameterModifier System.Reflection.ParameterModifier
System.Reflection.TypeAttributes
System.Reflection.TypeFilter System.Reflection.TypeFilter
System.Reflection.TargetParameterCountException System.Reflection.TargetParameterCountException
System.Reflection.TargetInvocationException System.Reflection.TargetInvocationException
@ -229,6 +235,7 @@ System.Runtime.CompilerServices.AsyncTaskCache
System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`* System.Runtime.CompilerServices.AsyncTaskMethodBuilder`*
System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder
System.Runtime.CompilerServices.FormattableStringFactory
System.Runtime.CompilerServices.IAsyncStateMachine System.Runtime.CompilerServices.IAsyncStateMachine
System.Runtime.CompilerServices.TaskAwaiter System.Runtime.CompilerServices.TaskAwaiter
System.Runtime.CompilerServices.TaskAwaiter`* System.Runtime.CompilerServices.TaskAwaiter`*

View File

@ -711,33 +711,34 @@ namespace system
// //
public static void Sort<T>(T[] array) public static void Sort<T>(T[] array)
=> SortGeneric(array, array, 0, -1, false, null); => SortGeneric<T>(array, array, 0, -1, false, null);
public static void Sort<T>(T[] array, system.collections.generic.IComparer<T> comparer) public static void Sort<T>(T[] array, IComparer<T> comparer)
=> SortGeneric(array, array, 0, -1, false, comparer); => SortGeneric<T>(array, array, 0, -1, false, comparer);
public static void Sort<T>(T[] array, int index, int length) public static void Sort<T>(T[] array, int index, int length)
=> SortGeneric(array, array, index, length, true, null); => SortGeneric<T>(array, array, index, length, true, null);
public static void Sort<T>(T[] array, int index, int length, system.collections.generic.IComparer<T> comparer) public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer)
=> SortGeneric(array, array, index, length, true, comparer); => SortGeneric<T>(array, array, index, length, true, comparer);
public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items) public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items)
=> SortGeneric(keys, items, 0, -1, false, null); => SortGeneric<TKey>(keys, items, 0, -1, false, null);
public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, system.collections.generic.IComparer<TKey> comparer) public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, IComparer<TKey> comparer)
=> SortGeneric(keys, items, 0, -1, false, comparer); => SortGeneric<TKey>(keys, items, 0, -1, false, comparer);
public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length) public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length)
=> SortGeneric(keys, items, index, length, true, null); => SortGeneric<TKey>(keys, items, index, length, true, null);
public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length, public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length,
system.collections.generic.IComparer<TKey> comparer) IComparer<TKey> comparer)
=> SortGeneric(keys, items, index, length, true, comparer); => SortGeneric<TKey>(keys, items, index, length, true, comparer);
private static void SortGeneric(object keys_, object items_, private static void SortGeneric<T>(object keys_, object items_,
int index, int length, bool haveLength, int index, int length,
java.util.Comparator comparer) bool haveLength,
IComparer<T> comparer)
{ {
ThrowIfNull(keys_); ThrowIfNull(keys_);
var keys = GetProxy(keys_); var keys = GetProxy(keys_);
@ -749,7 +750,8 @@ namespace system
ThrowIfNull(items_); ThrowIfNull(items_);
items = GetProxy(items_); items = GetProxy(items_);
} }
SortCommon(keys, items, index, length, haveLength, comparer); SortCommon(keys, items, index, length, haveLength,
new system.collections.GenericComparerProxy<T>(comparer));
} }
// //
@ -1463,10 +1465,9 @@ namespace system
// create the Array.Proxy<T> object, where T is the array element. // create the Array.Proxy<T> object, where T is the array element.
// note that we use reflection to call the constructor, which takes // note that we use reflection to call the constructor, which takes
// one additional hidden parameter for the generic type. // one additional hidden parameter for the generic type.
var elementType =
system.RuntimeType.GetType(objClass.getComponentType());
var newProxyObject = var newProxyObject =
ProxyConstructor.newInstance(new object[] { obj, elementType }); ProxyConstructor.newInstance(new object[] {
obj, GetArrayElementType(obj, objClass) });
proxy = ArrayProxyCache.GetOrAdd(obj, newProxyObject); proxy = ArrayProxyCache.GetOrAdd(obj, newProxyObject);
} }
if (ok == 2) if (ok == 2)
@ -1526,6 +1527,38 @@ namespace system
return (Array) (Array.ProxySyncRoot) proxy; return (Array) (Array.ProxySyncRoot) proxy;
} }
private static System.Type GetArrayElementType(
object arrayObject, java.lang.Class arrayClass)
{
// an array object does not keep generic type information
// for its component, so a Tuple<int,string>[] array becomes
// just Tuple$$2[]. casting to IEnumerable<Tuple<int,string>>
// would fail. below, we try to extract the concrete generic
// type from the first element in the array.
//
// first, check if the array component is a non-concrete
// generic type, and that the array has at least one element.
var elementType = system.RuntimeType.GetType(
arrayClass.getComponentType());
if ( elementType.IsGenericTypeDefinition
&& java.lang.reflect.Array.getLength(arrayObject) > 0)
{
// extract the first element, and make sure it is the same
// class as the array (so same number of generic argument)
var element0 = java.lang.reflect.Array.get(arrayObject, 0);
if ( (! object.ReferenceEquals(element0, null))
&& element0.GetType() is RuntimeType element0Type
&& ((RuntimeType) elementType).JavaClassForArray()
== element0Type.JavaClassForArray())
{
// return a concrete generic type
elementType = elementType.MakeGenericType(
element0Type.GetGenericArguments());
}
}
return elementType;
}
public static void MarkJagged(object obj) public static void MarkJagged(object obj)
{ {
((Array) GetProxy(obj, cachedArrayType, false)).jagged = true; ((Array) GetProxy(obj, cachedArrayType, false)).jagged = true;

View File

@ -3,30 +3,52 @@ namespace system.collections
{ {
[java.attr.AsInterface] [java.attr.AsInterface]
public abstract class IComparer : java.lang.Comparable, java.util.Comparator public abstract class IComparer : java.util.Comparator
{ {
public abstract int Compare(object x, object y); public abstract int Compare(object x, object y);
[java.attr.RetainName] [java.attr.RetainName]
public int compareTo(object obj) public int compare(object obj1, object obj2)
=> ((System.Collections.IComparer) this).Compare(this, obj); => this.Compare(obj1, obj2);
[java.attr.RetainName]
public bool equals(object otherComparer)
=> system.Object.Equals(this, otherComparer);
}
public class GenericComparerProxy<T> : java.util.Comparator
{
System.Collections.Generic.IComparer<T> comparer;
public GenericComparerProxy(System.Collections.Generic.IComparer<T> _comparer)
=> comparer = _comparer;
[java.attr.RetainName] [java.attr.RetainName]
public int compare(object obj1, object obj2) public int compare(object obj1, object obj2)
=> ((System.Collections.IComparer) this).Compare(obj1, obj2); => comparer.Compare((T) obj1, (T) obj2);
[java.attr.RetainName] [java.attr.RetainName]
public bool equals(object obj) public bool equals(object otherComparer)
=> ((System.Collections.IComparer) this).Compare(this, obj) == 0; => system.Object.Equals(this, otherComparer);
} }
} }
/*
removing custom IComparer<T>. the idea was to be able to send an
object implementing this interface to a Java method that takes a
java.util.Comparator. this works for the non-generic IComparer,
but will not work for the generic counterpart. instead we have
a bridge (GenericComparerProxy above) which takes an IComparer<T>
object and returns a java.util.Comparator. for an example usage,
see SortGeneric<T> in system.Array.
namespace system.collections.generic namespace system.collections.generic
{ {
[java.attr.AsInterface] [java.attr.AsInterface]
public abstract class IComparer<T> : java.lang.Comparable, java.util.Comparator public abstract class IComparer<T> : java.util.Comparator
{ {
// a generic variance field is created for this abstract class, // a generic variance field is created for this abstract class,
// see also GenericUtil::CreateGenericVarianceField() // see also GenericUtil::CreateGenericVarianceField()
@ -34,17 +56,12 @@ namespace system.collections.generic
public abstract int Compare(T x, T y); public abstract int Compare(T x, T y);
[java.attr.RetainName] [java.attr.RetainName]
public int compareTo(object obj) public int compare(object obj1, object obj2) => Compare(obj1, obj2);
=> ((System.Collections.Generic.IComparer<T>) this).Compare((T) (object) this, (T) obj);
[java.attr.RetainName]
public int compare(object obj1, object obj2)
=> ((System.Collections.Generic.IComparer<T>) this).Compare((T) obj1, (T) obj2);
[java.attr.RetainName] [java.attr.RetainName]
public bool equals(object obj) public bool equals(object obj)
=> ((System.Collections.Generic.IComparer<T>) this).Compare((T) (object) this, (T) obj) == 0; => throw new System.PlatformNotSupportedException();
} }
} }
*/

View File

@ -263,22 +263,26 @@ namespace system
static string FormatNames(java.lang.Class cls, long v, bool asFlags) static string FormatNames(java.lang.Class cls, long v, bool asFlags)
{ {
var enumLiteralFlags = ( java.lang.reflect.Modifier.PUBLIC
| java.lang.reflect.Modifier.STATIC
| java.lang.reflect.Modifier.FINAL);
var fields = cls.getDeclaredFields(); var fields = cls.getDeclaredFields();
int n = fields.Length; int n = fields.Length;
var s = asFlags var s = asFlags
? ToStringMulti(v, fields, n) ? ToStringMulti(v, fields, n, enumLiteralFlags)
: ToStringSingle(v, fields, n); : ToStringSingle(v, fields, n, enumLiteralFlags);
return s ?? java.lang.Long.toString(v); return s ?? java.lang.Long.toString(v);
} }
static string ToStringSingle(long v, java.lang.reflect.Field[] fields, int n) static string ToStringSingle(long v, java.lang.reflect.Field[] fields, int n,
int enumLiteralFlags)
{ {
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
var f = fields[i]; var f = fields[i];
if (f.getModifiers() == ( java.lang.reflect.Modifier.PUBLIC if (f.getModifiers() == enumLiteralFlags)
| java.lang.reflect.Modifier.STATIC))
{ {
f.setAccessible(true); f.setAccessible(true);
if (f.getLong(null) == v) if (f.getLong(null) == v)
@ -288,7 +292,8 @@ namespace system
return null; return null;
} }
static string ToStringMulti(long v, java.lang.reflect.Field[] fields, int n) static string ToStringMulti(long v, java.lang.reflect.Field[] fields, int n,
int enumLiteralFlags)
{ {
var v0 = v; var v0 = v;
var sb = new java.lang.StringBuilder(); var sb = new java.lang.StringBuilder();
@ -297,8 +302,7 @@ namespace system
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
var f = fields[i]; var f = fields[i];
if (f.getModifiers() == ( java.lang.reflect.Modifier.PUBLIC if (f.getModifiers() == enumLiteralFlags)
| java.lang.reflect.Modifier.STATIC))
{ {
f.setAccessible(true); f.setAccessible(true);
var fv = f.getLong(null); var fv = f.getLong(null);

View File

@ -89,7 +89,7 @@ namespace system
if (! ( proxyType is RuntimeType proxyRuntimeType if (! ( proxyType is RuntimeType proxyRuntimeType
&& proxyRuntimeType.IsCastableToGenericInterface(castToType, && proxyRuntimeType.IsCastableToGenericInterface(castToType,
(parentObject is system.Array.ProxySyncRoot parentArray) ))) (parentObject is system.Array.ProxySyncRoot) )))
{ {
return null; return null;
} }
@ -183,8 +183,8 @@ namespace system
public static void ThrowInvalidCastException(object objToCast, System.Type castToType) public static void ThrowInvalidCastException(object objToCast, System.Type castToType)
{ {
string msg = "Unable to cast object of type '" + objToCast.GetType().Name string msg = "Unable to cast object of type '" + objToCast.GetType()
+ "' to type '" + castToType.Name + "'."; + "' to type '" + castToType + "'.";
throw new InvalidCastException(msg); throw new InvalidCastException(msg);
} }
@ -200,8 +200,20 @@ namespace system
// this is a marker interface used by the system.RuntimeType constructor // this is a marker interface used by the system.RuntimeType constructor
// to identify generic types. this is implemented by interface types; // to identify generic types. this is implemented by interface types;
// class types implement IGenericObject, which derives from this type. // class types implement IGenericObject, which derives from this type.
// it also makes it easy to specify ProGuard rules for Android 'R8':
// -keepclassmembers class * implements system.IGenericEntity {
// public static final java.lang.String ?generic?variance;
// public static final *** ?generic?info?class;
// private system.RuntimeType ?generic?type;
// public static final *** ?generic?info?method (...);
// <init>(...);
// }
} }
interface IGenericObject : IGenericEntity interface IGenericObject : IGenericEntity
{ {
// Returns the type of a generic object (i.e. its '-generic-type' field). // Returns the type of a generic object (i.e. its '-generic-type' field).

View File

@ -1285,7 +1285,7 @@ namespace system.globalization {
var iterator = JavaCollator.getCollationElementIterator("" + ch0); var iterator = JavaCollator.getCollationElementIterator("" + ch0);
if (iterator.next() != 0) if (iterator.next() != 0)
{ {
throw new System.Globalization.CultureNotFoundException( throw new PlatformNotSupportedException(
"unexpected value for UNICODE NULL"); "unexpected value for UNICODE NULL");
} }
} }

View File

@ -99,6 +99,9 @@ namespace system.globalization {
public static CultureInfo CurrentCulture public static CultureInfo CurrentCulture
=> system.threading.Thread.CurrentThread.CurrentCulture; => system.threading.Thread.CurrentThread.CurrentCulture;
public static CultureInfo CurrentUICulture
=> system.threading.Thread.CurrentThread.CurrentCulture;
public static CultureInfo InvariantCulture => s_InvariantCultureInfo; public static CultureInfo InvariantCulture => s_InvariantCultureInfo;
public virtual object Clone() => throw new System.NotImplementedException(); public virtual object Clone() => throw new System.NotImplementedException();

View File

@ -9,7 +9,20 @@ namespace system
[java.attr.RetainType] private java.util.UUID _uuid; [java.attr.RetainType] private java.util.UUID _uuid;
private java.util.UUID uuid => _uuid ?? (_uuid = new java.util.UUID(0, 0)); private java.util.UUID uuid => _uuid ?? (_uuid = new java.util.UUID(0, 0));
public Guid(string g) => _uuid = java.util.UUID.fromString(g); public Guid(string g)
{
if (g.Length == 32)
{
var g1 = (java.lang.CharSequence) (object) g;
g = new java.lang.StringBuilder(36)
.append(g1, 0, 0 + 8).append('-')
.append(g1, 8, 8 + 4).append('-')
.append(g1, 12, 12 + 4).append('-')
.append(g1, 16, 16 + 4).append('-')
.append(g1, 20, 20 + 12).ToString();
}
_uuid = java.util.UUID.fromString(g);
}
public override string ToString() => uuid.ToString(); public override string ToString() => uuid.ToString();
public string ToString(string format) => ToString(format, null); public string ToString(string format) => ToString(format, null);

View File

@ -9,11 +9,7 @@ namespace system
public abstract int CompareTo(object obj); public abstract int CompareTo(object obj);
[java.attr.RetainName] [java.attr.RetainName]
public int compareTo(object obj) public int compareTo(object obj) => this.CompareTo(obj);
{
//return ((System.IComparable) this).CompareTo(obj);
return this.CompareTo(obj);
}
} }

View File

@ -49,10 +49,28 @@ namespace system
public static double Tan(double a) => java.lang.Math.tan(a); public static double Tan(double a) => java.lang.Math.tan(a);
public static double Tanh(double a) => java.lang.Math.tanh(a); public static double Tanh(double a) => java.lang.Math.tanh(a);
public static double Asin(double a) => java.lang.Math.asin(a);
public static double Asinh(double a) =>
java.lang.Math.log(a + java.lang.Math.sqrt(a * a + 1.0));
public static double Acos(double a) => java.lang.Math.acos(a);
public static double Acosh(double a) =>
java.lang.Math.log(a + java.lang.Math.sqrt(a * a - 1.0));
public static double Atan(double a) => java.lang.Math.atan(a);
public static double Atan2(double x, double y) => java.lang.Math.atan2(x, y);
public static double Atanh(double a) => 0.5 * java.lang.Math.log((1 + a) / (1 - a));
public static double Cbrt(double a) => java.lang.Math.cbrt(a);
public static double Exp(double a) => java.lang.Math.exp(a);
public static double Pow(double a, double b) => java.lang.Math.pow(a, b); public static double Pow(double a, double b) => java.lang.Math.pow(a, b);
public static double Sqrt(double a) => java.lang.Math.sqrt(a); public static double Sqrt(double a) => java.lang.Math.sqrt(a);
public static double ScaleB(double x, int n) => java.lang.Math.scalb(x, n); public static double ScaleB(double x, int n) => java.lang.Math.scalb(x, n);
public static double Ceiling(double a) => java.lang.Math.ceil(a);
public static double Floor(double a) => java.lang.Math.floor(a);
public static double IEEERemainder(double x, double y) =>
java.lang.Math.IEEEremainder(x, y);
public static double CopySign(double x, double y) => java.lang.Math.copySign(x, y);
public static double Round(double a) public static double Round(double a)
{ {
// java round clamps the values to Long.MIN_VALUE .. Long.MAX_VALUE // java round clamps the values to Long.MIN_VALUE .. Long.MAX_VALUE
@ -74,6 +92,187 @@ namespace system
public static void OverflowException() public static void OverflowException()
=> throw new System.OverflowException("Arithmetic operation resulted in an overflow."); => throw new System.OverflowException("Arithmetic operation resulted in an overflow.");
public static double BitDecrement(double a)
{
// java.lang.Math.nextDown(double)
if (java.lang.Double.isNaN(a) || a == java.lang.Double.NEGATIVE_INFINITY)
return a;
if (a == 0.0)
return -java.lang.Double.MIN_VALUE;
return java.lang.Double.longBitsToDouble(
java.lang.Double.doubleToRawLongBits(a) +
((a > 0.0) ? -1L : 1L));
}
public static double BitIncrement(double a)
{
// java.lang.Math.nextUp(double)
if (java.lang.Double.isNaN(a) || a == java.lang.Double.POSITIVE_INFINITY)
return a;
a += 0.0;
return java.lang.Double.longBitsToDouble(
java.lang.Double.doubleToRawLongBits(a) +
((a >= 0.0) ? 1L : -1L));
}
public static double MaxMagnitude(double x, double y)
{
var ax = java.lang.Math.abs(x);
var ay = java.lang.Math.abs(y);
return ( (ax > ay)
|| (ax == ay && x >= 0.0)
|| java.lang.Double.isNaN(ax)) ? x : y;
}
public static double MinMagnitude(double x, double y)
{
var ax = java.lang.Math.abs(x);
var ay = java.lang.Math.abs(y);
return ( (ax < ay)
|| (ax == ay && x < 0.0)
|| java.lang.Double.isNaN(ax)) ? x : y;
}
public static double Log(double a) => java.lang.Math.log(a);
public static double Log10(double a) => java.lang.Math.log10(a);
public static double Log2(double a) => java.lang.Math.log(a) / java.lang.Math.log(2.0);
public static int ILogB(double a) => (int) (java.lang.Math.log(a) / java.lang.Math.log(2.0));
public static double Log(double a, double b)
{
if (java.lang.Double.isNaN(a))
return a;
if (java.lang.Double.isNaN(b))
return b;
if ( (b == 1.0)
|| ( (a != 1.0)
&& ( b == 0.0
|| b == java.lang.Double.POSITIVE_INFINITY)))
return java.lang.Double.NaN;
return java.lang.Math.log(a) / java.lang.Math.log(b);
}
}
public static class MathF
{
public static int Sign(float a) => (int) java.lang.Math.signum(a);
public static float Abs(float a) => (a < 0) ? -a : a;
public static float Min(float a, float b) => (a <= b) ? a : b;
public static float Max(float a, float b) => (a >= b) ? a : b;
public static float Sin(float a) => (float) java.lang.Math.sin(a);
public static float Sinh(float a) => (float) java.lang.Math.sinh(a);
public static float Cos(float a) => (float) java.lang.Math.cos(a);
public static float Cosh(float a) => (float) java.lang.Math.cosh(a);
public static float Tan(float a) => (float) java.lang.Math.tan(a);
public static float Tanh(float a) => (float) java.lang.Math.tanh(a);
public static float Asin(float a) => (float) java.lang.Math.asin(a);
public static float Asinh(float a) => (float)
java.lang.Math.log(a + java.lang.Math.sqrt((double) a * a + 1.0));
public static float Acos(float a) => (float) java.lang.Math.acos(a);
public static float Acosh(float a) => (float)
java.lang.Math.log(a + java.lang.Math.sqrt((double) a * a - 1.0));
public static float Atan(float a) => (float) java.lang.Math.atan(a);
public static float Atan2(float x, double y) => (float) java.lang.Math.atan2(x, y);
public static float Atanh(float a) =>
(float) java.lang.Math.log((1 + a) / (1 - a)) * 0.5f;
public static float Cbrt(float a) => (float) java.lang.Math.cbrt(a);
public static float Exp(float a) => (float) java.lang.Math.exp(a);
public static float Pow(float a, float b) => (float) java.lang.Math.pow(a, b);
public static float Sqrt(float a) => (float) java.lang.Math.sqrt(a);
public static float ScaleB(float x, int n) => (float) java.lang.Math.scalb(x, n);
public static float Ceiling(float a) => (float) java.lang.Math.ceil(a);
public static float Floor(float a) => (float) java.lang.Math.floor(a);
public static float IEEERemainder(float x, float y) =>
(float) java.lang.Math.IEEEremainder(x, y);
public static float CopySign(float x, float y) => java.lang.Math.copySign(x, y);
public static float Round(float a)
{
// java round clamps the values to Long.MIN_VALUE .. Long.MAX_VALUE
// so we have to use floor rather than round
if (a > System.Int64.MaxValue)
return (float) java.lang.Math.floor(a + 0.5);
else if (a < System.Int64.MinValue)
return (float) java.lang.Math.floor(a - 0.5);
else
return (float) java.lang.Math.round(a);
}
public static float Truncate(float a)
{
if (! (java.lang.Float.isInfinite(a) || java.lang.Float.isNaN(a)))
a = (float) ((long) a);
return a;
}
public static float BitDecrement(float a)
{
// java.lang.Math.nextDown(float)
if (java.lang.Float.isNaN(a) || a == java.lang.Float.NEGATIVE_INFINITY)
return a;
if (a == 0.0f)
return -java.lang.Float.MIN_VALUE;
return java.lang.Float.intBitsToFloat(
java.lang.Float.floatToRawIntBits(a) +
((a > 0.0f) ? -1 : 1));
}
public static float BitIncrement(float a)
{
// java.lang.Math.nextUp(float)
if (java.lang.Float.isNaN(a) || a == java.lang.Float.POSITIVE_INFINITY)
return a;
a += 0.0f;
return java.lang.Float.intBitsToFloat(
java.lang.Float.floatToRawIntBits(a) +
((a >= 0.0f) ? 1 : -1));
}
public static float MaxMagnitude(float x, float y)
{
var ax = java.lang.Math.abs(x);
var ay = java.lang.Math.abs(y);
return ( (ax > ay)
|| (ax == ay && x >= 0.0f)
|| java.lang.Float.isNaN(ax)) ? x : y;
}
public static float MinMagnitude(float x, float y)
{
var ax = java.lang.Math.abs(x);
var ay = java.lang.Math.abs(y);
return ( (ax < ay)
|| (ax == ay && x < 0.0f)
|| java.lang.Float.isNaN(ax)) ? x : y;
}
public static float Log(float a) => (float) java.lang.Math.log(a);
public static float Log10(float a) => (float) java.lang.Math.log10(a);
public static float Log2(float a) => (float) (java.lang.Math.log(a) / java.lang.Math.log(2.0));
public static int ILogB(float a) => (int) (java.lang.Math.log(a) / java.lang.Math.log(2.0));
public static float Log(float a, float b)
{
if (java.lang.Float.isNaN(a))
return a;
if (java.lang.Float.isNaN(b))
return b;
if ( (b == 1.0f)
|| ( (a != 1.0f)
&& ( b == 0.0f
|| b == java.lang.Float.POSITIVE_INFINITY)))
return java.lang.Float.NaN;
return (float) (java.lang.Math.log(a) / java.lang.Math.log(b));
}
} }
} }

View File

@ -0,0 +1,124 @@
namespace system.reflection
{
public static class FSharpCompat
{
// we don't yet translate .Net attributes to Java annotations,
// so we have to fake some attributes to support F# ToString()
public static object[] GetCustomAttributes_Type(java.lang.Class scanClass,
java.lang.Class attrClass)
{
// we manufacture a CompilationMapping attribute for a type
// type implements at least three specific interfaces.
// if it has a nested Tags class, it is a (discriminated union)
// SumType; otherwise it is a plain RecordType.
if (IsFSharpAttr(attrClass) && IsFSharpType(scanClass))
{
int sourceConstructFlags = IsFSharpSumType(scanClass)
? /* SumType */ 1 : /* RecordType */ 2;
return CreateAttr(attrClass, sourceConstructFlags);
}
return null;
}
public static object[] GetCustomAttributes_Property(java.lang.Class declClass,
java.lang.Class attrClass)
{
if (IsFSharpAttr(attrClass))
{
// we manufacture a CompilationMapping attribute for a property:
// - if declared directly in a class that is an "F# type"
// and is not also a SumType
// - if declared in an inner class, and the outer class
// is an "F# type" that is a SumType
bool isFSharp = IsFSharpType(declClass);
if (isFSharp)
{
if (IsFSharpSumType(declClass))
isFSharp = false;
}
else
{
var outerClass = declClass.getDeclaringClass();
if (outerClass != null)
isFSharp = IsFSharpType(outerClass);
}
if (isFSharp)
{
return CreateAttr(attrClass, /* Field */ 4);
}
return RuntimeType.EmptyObjectArray;
}
return null;
}
private static bool IsFSharpType(java.lang.Class scanClass)
{
int requiredInterfaceCount = 0;
foreach (var ifc in scanClass.getInterfaces())
{
if ( ifc == (java.lang.Class)
typeof(System.Collections.IStructuralEquatable)
|| ifc == (java.lang.Class)
typeof(System.Collections.IStructuralComparable)
|| ifc == (java.lang.Class) typeof(System.IComparable))
requiredInterfaceCount++;
}
return (requiredInterfaceCount >= 3);
}
private static bool IsFSharpSumType(java.lang.Class scanClass)
=> system.RuntimeType.FindInnerClass(scanClass, "Tags") != null;
private static bool IsFSharpAttr(java.lang.Class attrClass)
=> attrClass == (java.lang.Class)
typeof(Microsoft.FSharp.Core.CompilationMappingAttribute);
private static object[] CreateAttr(java.lang.Class attrClass,
int sourceConstructFlags)
{
foreach (var _constr in attrClass.getConstructors())
{
#pragma warning disable 0436
var constr = (java.lang.reflect.Constructor) (object) _constr;
#pragma warning restore 0436
var parameters = constr.getParameterTypes();
if ( parameters.Length == 2
&& parameters[0] == java.lang.Integer.TYPE
&& parameters[1].isMemberClass())
{
var created = constr.newInstance(new object[] {
java.lang.Integer.valueOf(sourceConstructFlags),
null
});
return new object[] { created };
}
}
return null;
}
}
}
namespace Microsoft.FSharp.Core
{
[java.attr.Discard] // discard in output
public class CompilationMappingAttribute { }
}

View File

@ -51,7 +51,7 @@ namespace system.reflection
public bool IsPrivate => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private; public bool IsPrivate => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private;
public bool IsFamily => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family; public bool IsFamily => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family;
public bool IsAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly; public bool IsAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly;

View File

@ -99,6 +99,12 @@ namespace system.reflection
throw new NotImplementedException("Assembly.GetCustomAttributes"); throw new NotImplementedException("Assembly.GetCustomAttributes");
} }
//
// Properties
//
public override bool ReflectionOnly => false;
// //
// ISerializable // ISerializable
// //

View File

@ -127,7 +127,7 @@ namespace system.reflection
object[] parameters, CultureInfo culture) object[] parameters, CultureInfo culture)
{ {
if (parameters == null) if (parameters == null)
parameters = new object[0]; parameters = system.RuntimeType.EmptyObjectArray;
int numParameters = parameters.Length; int numParameters = parameters.Length;
if (HasUniqueArg) if (HasUniqueArg)

View File

@ -12,6 +12,7 @@ namespace system.reflection
{ {
[java.attr.RetainType] public java.lang.reflect.Field JavaField; [java.attr.RetainType] public java.lang.reflect.Field JavaField;
[java.attr.RetainType] public system.RuntimeType reflectedType; [java.attr.RetainType] public system.RuntimeType reflectedType;
[java.attr.RetainType] private FieldAttributes cachedAttrs = 0;
// //
// constructor // constructor
@ -30,69 +31,83 @@ namespace system.reflection
public static FieldInfo[] GetFields(BindingFlags bindingAttr, RuntimeType initialType) public static FieldInfo[] GetFields(BindingFlags bindingAttr, RuntimeType initialType)
{ {
var list = new System.Collections.Generic.List<FieldInfo>(); var list = new java.util.ArrayList();
BindingFlagsIterator.Run(bindingAttr, initialType, MemberTypes.Field, BindingFlagsIterator.Run(bindingAttr & ~BindingFlags.GetField,
initialType, MemberTypes.Field,
(javaAccessibleObject) => (javaAccessibleObject) =>
{ {
var javaField = (java.lang.reflect.Field) javaAccessibleObject; var javaField = (java.lang.reflect.Field) javaAccessibleObject;
javaField.setAccessible(true); javaField.setAccessible(true);
list.Add(new RuntimeFieldInfo(javaField, initialType)); list.add(new RuntimeFieldInfo(javaField, initialType));
return true; return true;
}); });
return list.ToArray(); return (RuntimeFieldInfo[]) list.toArray(new RuntimeFieldInfo[0]);
} }
//
//
//
public override System.Type FieldType public override System.Type FieldType
=> system.RuntimeType.GetType(JavaField.getType()); => system.RuntimeType.GetType(JavaField.getType());
//
//
//
public override object GetValue(object obj) public override object GetValue(object obj)
=> throw new PlatformNotSupportedException(); => system.RuntimeType.UnboxJavaReturnValue(JavaField.get(obj));
public override void SetValue(object obj, object value, BindingFlags invokeAttr, public override void SetValue(object obj, object value, BindingFlags invokeAttr,
Binder binder, CultureInfo culture) Binder binder, CultureInfo culture)
=> throw new PlatformNotSupportedException(); => throw new PlatformNotSupportedException();
public override System.Reflection.FieldAttributes Attributes public override FieldAttributes Attributes
=> throw new PlatformNotSupportedException(); {
get
{
var attrs = cachedAttrs;
if (attrs == 0)
{
int modifiers = JavaField.getModifiers();
if ((modifiers & java.lang.reflect.Modifier.PUBLIC) != 0)
attrs |= FieldAttributes.Public;
if ((modifiers & java.lang.reflect.Modifier.PRIVATE) != 0)
attrs |= FieldAttributes.Private;
if ((modifiers & java.lang.reflect.Modifier.PROTECTED) != 0)
attrs |= FieldAttributes.Family;
if ((modifiers & java.lang.reflect.Modifier.TRANSIENT) != 0)
attrs |= FieldAttributes.NotSerialized;
public override System.Type DeclaringType if ((modifiers & java.lang.reflect.Modifier.STATIC) != 0)
=> throw new PlatformNotSupportedException(); {
attrs |= FieldAttributes.Static;
if ( ((modifiers & java.lang.reflect.Modifier.FINAL) != 0)
&& JavaField.getType().isPrimitive()
&& JavaField.get(null) != null)
{
attrs |= FieldAttributes.Literal;
}
}
public override System.Type ReflectedType cachedAttrs = attrs;
=> throw new PlatformNotSupportedException(); }
return attrs;
}
}
public override Type ReflectedType => reflectedType;
public override Type DeclaringType
=> system.RuntimeType.GetType(JavaField.getDeclaringClass());
public override string Name => JavaField.getName(); public override string Name => JavaField.getName();
public override object GetRawConstantValue() public override object GetRawConstantValue()
{ {
if ((JavaField.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) if (0 != (JavaField.getModifiers() & ( java.lang.reflect.Modifier.STATIC
| java.lang.reflect.Modifier.FINAL))
&& JavaField.getType().isPrimitive())
{ {
var value = JavaField.get(null); return GetValue(null);
switch (value)
{
case java.lang.Boolean boolBox:
return system.Boolean.Box(boolBox.booleanValue() ? 1 : 0);
case java.lang.Byte byteBox:
return system.SByte.Box(byteBox.byteValue());
case java.lang.Character charBox:
return system.Char.Box(charBox.charValue());
case java.lang.Short shortBox:
return system.Int16.Box(shortBox.shortValue());
case java.lang.Integer intBox:
return system.Int32.Box(intBox.intValue());
case java.lang.Long longBox:
return system.Int64.Box(longBox.longValue());
case java.lang.Float floatBox:
return system.Single.Box(floatBox.floatValue());
case java.lang.Double doubleBox:
return system.Double.Box(doubleBox.doubleValue());
}
} }
throw new System.NotSupportedException(); throw new System.NotSupportedException();
} }

View File

@ -50,7 +50,7 @@ namespace system.reflection
string originalName = javaMethod.getName(); string originalName = javaMethod.getName();
var compareName = RuntimeMethodInfo.AdjustedMethodName(originalName); var compareName = RuntimeMethodInfo.AdjustedMethodName(originalName);
if (name == compareName) if (name == compareName && CompareParameters(javaMethod, types))
{ {
javaMethod.setAccessible(true); javaMethod.setAccessible(true);
var jmodifiers = javaMethod.getModifiers(); var jmodifiers = javaMethod.getModifiers();
@ -63,6 +63,26 @@ namespace system.reflection
}); });
return foundMethod; return foundMethod;
#pragma warning disable 0436
static bool CompareParameters(java.lang.reflect.Method javaMethod, Type[] types)
{
if (types == null)
return true;
var paramTypes = javaMethod.getParameterTypes();
if (types.Length != paramTypes.Length)
return false;
for (int i = 0; i < paramTypes.Length; i++)
{
if (! object.ReferenceEquals(
types[i], system.RuntimeType.GetType(paramTypes[i])))
return false;
}
return true;
}
#pragma warning restore 0436
} }
// //
@ -76,8 +96,6 @@ namespace system.reflection
throw new PlatformNotSupportedException("non-null binder"); throw new PlatformNotSupportedException("non-null binder");
if (callConvention != CallingConventions.Any) if (callConvention != CallingConventions.Any)
throw new PlatformNotSupportedException("calling convention must be Any"); throw new PlatformNotSupportedException("calling convention must be Any");
if (types != null && types.Length != 0)
throw new PlatformNotSupportedException("non-null types");
if (modifiers != null) if (modifiers != null)
throw new PlatformNotSupportedException("non-null modifiers"); throw new PlatformNotSupportedException("non-null modifiers");
} }
@ -337,7 +355,7 @@ namespace system.reflection
} }
// //
// // GetParameters
// //
public override ParameterInfo[] GetParameters() public override ParameterInfo[] GetParameters()

View File

@ -13,7 +13,7 @@ namespace system.reflection
{ {
[java.attr.RetainType] public java.security.ProtectionDomain JavaDomain; [java.attr.RetainType] public java.security.ProtectionDomain JavaDomain;
public override System.Type[] GetTypes() => new System.Type[0]; public override System.Type[] GetTypes() => system.RuntimeType.EmptyTypeArray;
public MetadataImport MetadataImport => _MetadataImport; public MetadataImport MetadataImport => _MetadataImport;
public StructLayoutAttribute StructLayoutAttribute => _StructLayoutAttribute; public StructLayoutAttribute StructLayoutAttribute => _StructLayoutAttribute;

View File

@ -0,0 +1,275 @@
using System;
using System.Reflection;
using System.Globalization;
using System.Runtime.Serialization;
namespace system.reflection
{
[System.Serializable]
public sealed class RuntimePropertyInfo : PropertyInfo, ISerializable
{
#pragma warning disable 0436
[java.attr.RetainType] private java.lang.reflect.Method JavaGetMethod;
[java.attr.RetainType] private java.lang.reflect.Method JavaSetMethod;
#pragma warning restore 0436
[java.attr.RetainType] private string propertyName;
[java.attr.RetainType] private system.RuntimeType propertyType;
[java.attr.RetainType] private system.RuntimeType reflectedType;
[java.attr.RetainType] private system.RuntimeType declaringType;
//
// GetMethod (called by system.RuntimeType.GetPropertyImpl)
//
public static PropertyInfo GetProperty(string name, BindingFlags bindingAttr,
Binder binder, Type returnType,
Type[] types, ParameterModifier[] modifiers,
RuntimeType initialType)
{
ThrowHelper.ThrowIfNull(name);
if (types != null && types.Length != 0) // if indexed property
throw new PlatformNotSupportedException();
#pragma warning disable 0436
java.lang.reflect.Method getMethod = null;
java.lang.Class getClass = null;
java.lang.reflect.Method setMethod = null;
java.lang.Class setClass = null;
#pragma warning restore 0436
BindingFlagsIterator.Run(bindingAttr & ~BindingFlags.GetProperty,
initialType, MemberTypes.Method,
(javaAccessibleObject) =>
{
#pragma warning disable 0436
var javaMethod = (java.lang.reflect.Method) javaAccessibleObject;
#pragma warning restore 0436
var cls = IsGetMethod(javaMethod, name, returnType);
if (cls != null)
{
getMethod = javaMethod;
getClass = cls;
}
else
{
cls = IsSetMethod(javaMethod, name, returnType);
if (cls != null)
{
setMethod = javaMethod;
setClass = cls;
}
}
return true;
});
if (getClass != null)
{
if (setClass != null && setClass != getClass)
setMethod = null;
}
else if (setClass != null)
{
getClass = setClass;
}
else // neither get nor set methods
return null;
return new RuntimePropertyInfo(getMethod, setMethod, getClass, initialType);
#pragma warning disable 0436
static java.lang.Class IsGetMethod(java.lang.reflect.Method javaMethod,
string propertyName, Type propertyType)
{
if (javaMethod.getName() == "get_" + propertyName)
{
javaMethod.setAccessible(true);
var returnClass = javaMethod.getReturnType();
if ( object.ReferenceEquals(propertyType, null)
|| object.ReferenceEquals(propertyType,
system.RuntimeType.GetType(returnClass)))
{
return returnClass;
}
}
return null;
}
static java.lang.Class IsSetMethod(java.lang.reflect.Method javaMethod,
string propertyName, Type propertyType)
{
if (javaMethod.getName() == "set_" + propertyName)
{
javaMethod.setAccessible(true);
var paramClasses = javaMethod.getParameterTypes();
if (paramClasses.Length == 1)
{
var paramClass = paramClasses[0];
if ( object.ReferenceEquals(propertyType, null)
|| object.ReferenceEquals(propertyType,
system.RuntimeType.GetType(paramClass)))
{
return paramClass;
}
}
}
return null;
}
#pragma warning restore 0436
}
//
// GetProperties (called by system.RuntimeType.GetProperties()
//
public static PropertyInfo[] GetProperties(BindingFlags bindingAttr,
RuntimeType initialType)
{
var list = new java.util.ArrayList();
BindingFlagsIterator.Run(bindingAttr & ~BindingFlags.GetProperty,
initialType, MemberTypes.Method,
(javaAccessibleObject) =>
{
#pragma warning disable 0436
var javaMethod = (java.lang.reflect.Method) javaAccessibleObject;
#pragma warning restore 0436
if (javaMethod.getName().StartsWith("get_"))
{
javaMethod.setAccessible(true);
var returnClass = javaMethod.getReturnType();
if ( returnClass != java.lang.Void.TYPE
&& javaMethod.getParameterTypes().Length == 0)
{
list.add(new RuntimePropertyInfo(
javaMethod, null, returnClass, initialType));
}
}
return true;
});
return (RuntimePropertyInfo[]) list.toArray(new RuntimePropertyInfo[0]);
}
//
// constructor
//
#pragma warning disable 0436
private RuntimePropertyInfo(java.lang.reflect.Method javaGetMethod,
java.lang.reflect.Method javaSetMethod,
java.lang.Class javaClass,
system.RuntimeType reflectedType)
#pragma warning restore 0436
{
var getOrSetMethod = javaGetMethod ?? javaSetMethod;
propertyName = getOrSetMethod.getName().Substring(4);
propertyType = (system.RuntimeType) system.RuntimeType.GetType(javaClass);
this.JavaGetMethod = javaGetMethod;
this.JavaSetMethod = javaSetMethod;
this.reflectedType = reflectedType;
this.declaringType = (system.RuntimeType) system.RuntimeType.GetType(
getOrSetMethod.getDeclaringClass());
}
//
//
//
public override PropertyAttributes Attributes
=> throw new PlatformNotSupportedException();
public override MethodInfo[] GetAccessors(bool nonPublic)
=> throw new PlatformNotSupportedException();
public override MethodInfo GetGetMethod(bool nonPublic)
=> throw new PlatformNotSupportedException();
public override MethodInfo GetSetMethod(bool nonPublic)
=> throw new PlatformNotSupportedException();
public override bool CanRead => (JavaGetMethod != null);
public override bool CanWrite => (JavaSetMethod != null);
//
//
//
public override Type PropertyType => propertyType;
public override ParameterInfo[] GetIndexParameters()
=> throw new PlatformNotSupportedException();
public override object GetValue(object obj, BindingFlags invokeAttr,
Binder binder, object[] index, CultureInfo culture)
{
invokeAttr &= ~( BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.GetProperty);
if (invokeAttr != BindingFlags.Default)
throw new PlatformNotSupportedException("bad binding flags " + invokeAttr);
if (binder != null)
throw new PlatformNotSupportedException("non-null binder");
if (culture != null)
throw new PlatformNotSupportedException("non-null culture");
if (index != null || JavaGetMethod == null)
throw new ArgumentException();
return system.RuntimeType.UnboxJavaReturnValue(
JavaGetMethod.invoke(obj, null));
}
public override void SetValue(object obj, object value, BindingFlags invokeAttr,
Binder binder, object[] index, CultureInfo culture)
=> throw new PlatformNotSupportedException();
//
//
//
public override Type ReflectedType => reflectedType;
public override Type DeclaringType => declaringType;
public override string Name => propertyName;
//
// custom attributes
//
public override bool IsDefined(Type attributeType, bool inherit)
=> throw new PlatformNotSupportedException();
public override object[] GetCustomAttributes(bool inherit)
=> throw new PlatformNotSupportedException();
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
if (attributeType is system.RuntimeType attributeRuntimeType)
{
// we don't yet translate .Net attributes to Java annotations,
// so we have to fake some attributes to support F# ToString()
var attrs = system.reflection.FSharpCompat.GetCustomAttributes_Property(
declaringType.JavaClassForArray(),
attributeRuntimeType.JavaClassForArray());
if (attrs != null)
return attrs;
}
throw new PlatformNotSupportedException();
}
//
// ISerializable
//
public void GetObjectData(SerializationInfo info, StreamingContext context)
=> throw new PlatformNotSupportedException();
}
}

View File

@ -0,0 +1,564 @@
using System;
using System.Reflection;
using System.Globalization;
using System.Runtime.Serialization;
namespace system
{
//
// System/RuntimeType.cs contains methods required by the type
// system. System/Reflection/RuntimeType2.cs (this file) contains
// methods to provide run-time reflection support.
//
public partial class RuntimeType
{
//
// FullName
//
public override string FullName
{
get
{
string name;
var declType = DeclaringType;
if (! object.ReferenceEquals(declType, null))
name = declType.FullName + "+";
else if (IsGenericParameter)
return null;
else
{
name = Namespace;
if (name != null)
name += ".";
}
name += Name;
return name;
}
}
//
// Name
//
public override string Name
{
get
{
var generic = Generic;
if (JavaClass == null)
return GenericParameterName(generic);
if (JavaClass.isArray())
return (GetType(JavaClass.getComponentType()).Name) + "[]";
else
return ClassName(generic);
string ClassName(GenericData generic)
{
var name = JavaClass.getSimpleName();
int numArgsInDeclType = 0;
var declType = DeclaringType;
if (declType is RuntimeType declRuntimeType && declRuntimeType.Generic != null)
{
// a nested generic type duplicates the parameters of the parent
numArgsInDeclType = declRuntimeType.Generic.ArgumentTypes.Length;
}
if (generic != null)
{
int numArgs = generic.ArgumentTypes.Length;
int index = name.LastIndexOf("$$" + numArgs);
if (index != -1)
name = name.Substring(0, index);
numArgs -= numArgsInDeclType;
if (numArgs > 0)
name += "`" + numArgs.ToString();
}
return name;
}
string GenericParameterName(GenericData generic)
{
if (generic != null)
{
// generic parameter
var argTypes = generic.PrimaryType.Generic.ArgumentTypes;
for (int i = 0; i < argTypes.Length; i++)
{
if (object.ReferenceEquals(argTypes[i], this))
{
var typeNames = generic.PrimaryType.JavaClass.getTypeParameters();
if (i < typeNames.Length)
{
// if we have a valid generic signature, extract parameter name
var nm = typeNames[i].getName();
if (! string.IsNullOrEmpty(nm))
return nm;
}
return "T" + i.ToString();
}
}
}
return null; // invalid combination
}
}
}
//
// Namespace
//
public override string Namespace
{
get
{
var javaClass = JavaClass;
if (javaClass == null)
return null;
if (javaClass.isArray())
{
do
{
javaClass = javaClass.getComponentType();
}
while (javaClass.isArray());
return GetType(javaClass).Namespace;
}
string dottedName = javaClass.getName();
int lastIndex = dottedName.LastIndexOf('.');
if (lastIndex <= 0)
return null;
int firstIndex = 0;
string resultName = "";
for (;;)
{
int nextIndex = dottedName.IndexOf('.', firstIndex);
var component = Char.ToUpperInvariant(dottedName[firstIndex])
+ dottedName.Substring(firstIndex + 1, nextIndex - firstIndex - 1);
resultName += component;
if (nextIndex == lastIndex)
return resultName;
resultName += ".";
firstIndex = nextIndex + 1;
}
}
}
//
// DeclaringType
//
public override Type DeclaringType
{
get
{
var javaClass = JavaClass;
if (javaClass == null)
return null;
if (javaClass.isArray())
javaClass = javaClass.getComponentType();
var declClass = javaClass.getDeclaringClass();
return (declClass == null) ? null : GetType(declClass);
}
}
//
// ReflectedType
//
public override Type ReflectedType => DeclaringType;
//
// DeclaringMethod
//
public override MethodBase DeclaringMethod
=> throw new PlatformNotSupportedException();
//
// BaseType
//
public override Type BaseType
{
get
{
if ( JavaClass == null
|| object.ReferenceEquals(this, ObjectType)
|| IsInterface(this))
{
return null;
}
if (object.ReferenceEquals(this, ExceptionType))
{
// CilType translates System.Exception to java.lang.Throwable,
// and here we map java.lang.Throwable to system.Exception,
// thereby creating an infinite loop in which system.Exception
// inherits from java.lang.Exception, which inherits from
// java.lang.Throwable, which is mapped to system.Exception.
// the workaround breaks the infinite loop.
return ObjectType;
}
return FromJavaGenericType(JavaClass.getGenericSuperclass());
}
}
//
// Module
//
public override Module Module => Assembly.GetModules(false)[0];
//
// Assembly
//
public override Assembly Assembly => system.reflection.RuntimeAssembly.CurrentAssembly;
//
// AssemblyQualifiedName
//
public override string AssemblyQualifiedName
{
get
{
var name = FullName;
if (name != null)
name = Assembly.CreateQualifiedName(Assembly.FullName, name);
return name;
}
}
//
// GetCustomAttributes
//
public override object[] GetCustomAttributes(bool inherit) => EmptyObjectArray;
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
if (attributeType is system.RuntimeType attributeRuntimeType)
{
// we don't yet translate .Net attributes to Java annotations,
// so we have to fake some attributes to support F# ToString()
var attrs = system.reflection.FSharpCompat.GetCustomAttributes_Type(
this.JavaClass, attributeRuntimeType.JavaClass);
if (attrs != null)
return attrs;
}
return EmptyObjectArray;
}
//
// IsDefined
//
public override bool IsDefined(Type attributeType, bool inherit) => false;
//
// GetInterfaces
//
public override Type[] GetInterfaces()
{
var output = new java.util.HashSet();
GetInterfacesInternal(JavaClass, output);
return (Type[]) output.toArray(System.Type.EmptyTypes);
void GetInterfacesInternal(java.lang.Class input, java.util.HashSet output)
{
foreach (var javaType in input.getGenericInterfaces())
{
output.add(FromJavaGenericType(javaType));
if (javaType is java.lang.Class classType)
GetInterfacesInternal(classType, output);
}
}
}
//
// GetInterface
//
public override Type GetInterface(string name, bool ignoreCase)
{
if (name == null)
throw new ArgumentNullException();
StringComparison cmp = ignoreCase
? System.StringComparison.InvariantCultureIgnoreCase
: System.StringComparison.InvariantCulture;
foreach (var ifc in GetInterfaces())
{
if (string.Compare(name, ifc.FullName, cmp) == 0)
return ifc;
}
return null;
}
//
// GetNestedTypes
//
public override Type[] GetNestedTypes(BindingFlags bindingAttr)
{
bool takePublic = (bindingAttr & BindingFlags.Public) != 0;
bool takeNonPublic = (bindingAttr & BindingFlags.NonPublic) != 0;
if (takePublic || takeNonPublic)
{
var innerClasses = JavaClass.getDeclaredClasses();
if (innerClasses.Length > 0)
{
var list = new java.util.ArrayList();
for (int i = 0; i < innerClasses.Length; i++)
{
var innerCls = innerClasses[i];
var isPublic = (0 != (innerCls.getModifiers()
& java.lang.reflect.Modifier.PUBLIC));
if (takePublic == isPublic || takeNonPublic != isPublic)
{
var innerType = GetType(innerCls);
var generic = ((RuntimeType) innerType).Generic;
list.add(generic != null ? generic.PrimaryType : innerType);
}
}
return (Type[]) list.toArray(system.RuntimeType.EmptyTypeArray);
}
}
return system.RuntimeType.EmptyTypeArray;
}
//
// GetNestedType
//
public override Type GetNestedType(string name, BindingFlags bindingAttr)
{
ThrowHelper.ThrowIfNull(name);
if ((bindingAttr & BindingFlags.IgnoreCase) != 0)
throw new PlatformNotSupportedException();
var innerCls = FindInnerClass(JavaClass, name);
if (innerCls != null)
{
bool takePublic = (bindingAttr & BindingFlags.Public) != 0;
bool takeNonPublic = (bindingAttr & BindingFlags.NonPublic) != 0;
var isPublic = (0 != (innerCls.getModifiers()
& java.lang.reflect.Modifier.PUBLIC));
if (takePublic == isPublic || takeNonPublic != isPublic)
{
var innerType = GetType(innerCls);
var generic = ((RuntimeType) innerType).Generic;
return (generic != null ? generic.PrimaryType : innerType);
}
}
return null;
}
//
// FindInnerClass (public helper)
//
public static java.lang.Class FindInnerClass(
java.lang.Class javaClass, string name)
{
var innerClasses = javaClass.getDeclaredClasses();
for (int i = 0; i < innerClasses.Length; i++)
{
var innerCls = innerClasses[i];
if (innerCls.getSimpleName() == name)
return innerCls;
}
return null;
}
//
// Reflection on members of the type
//
public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
protected override PropertyInfo GetPropertyImpl(
string name, BindingFlags bindingAttr, Binder binder, Type returnType,
Type[] types, ParameterModifier[] modifiers)
{
if (JavaClass == null) // if generic parameter
return null;
return system.reflection.RuntimePropertyInfo.GetProperty(
name, bindingAttr, binder, returnType, types, modifiers, this);
}
protected override MethodInfo GetMethodImpl(
string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
Type[] types, ParameterModifier[] modifiers)
{
if (JavaClass == null) // if generic parameter
return null;
return system.reflection.RuntimeMethodInfo.GetMethod(
name, bindingAttr, binder, callConvention, types, modifiers, this);
}
protected override ConstructorInfo GetConstructorImpl(
BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
Type[] types, ParameterModifier[] modifiers)
{
if (JavaClass == null) // if generic parameter
return null;
return system.reflection.RuntimeConstructorInfo.GetConstructor(
bindingAttr, binder, callConvention, types, modifiers, this);
}
//
//
//
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
public override EventInfo[] GetEvents(BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
=> system.reflection.RuntimeFieldInfo.GetFields(bindingAttr, this);
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
=> system.reflection.RuntimePropertyInfo.GetProperties(bindingAttr, this);
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
//
//
//
public override object InvokeMember(
string name, BindingFlags invokeAttr, Binder binder, object target, object[] args,
ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
throw new PlatformNotSupportedException();
}
//
// reflection support - extract information from Java generic signatures
// (generated by MakeGenericSignature in GenericUtil)
//
Type FromJavaGenericType(java.lang.reflect.Type javaType)
{
if (javaType is java.lang.reflect.ParameterizedType parameterizedType)
{
// a ParameterizedType is GenericType<GenericArg1,...GenericArgN>
// where each GenericArg would be a TypeVariable or a Class
var primaryClass = (java.lang.Class) parameterizedType.getRawType();
var javaTypeArgs = parameterizedType.getActualTypeArguments();
int n = javaTypeArgs.Length;
var typeArgs = new Type[n];
for (int i = 0; i < n; i++)
typeArgs[i] = FromJavaGenericType(javaTypeArgs[i]);
return GetType(primaryClass, typeArgs);
}
if ( javaType is java.lang.reflect.TypeVariable varType
&& varType.getGenericDeclaration() is java.lang.Class varClass)
{
// a TypeVariable is a GenericArg with a reference to the type
// it belongs to. if that type (in the java generics mechanism)
// corresponds to 'this' type, and if 'this' type is a concrete
// generic instance (as opposed to a generic definition), then
// we grab a concrete generic type from 'this' type instance.
//
// for example, with A<T1,T2> and B<T1,T2> : A<T2,T1>, then
// for generic instance B<int,bool>, we are able to resolve
// the base type as A<bool,int>.
var varName = varType.getName();
var primaryType = GetType(varClass) as RuntimeType;
var argTypes = primaryType?.Generic?.ArgumentTypes;
if (argTypes != null)
{
int n = argTypes.Length;
for (int i = 0; i < n; i++)
{
if (argTypes[i].Name == varName)
{
if ( varClass == JavaClass
&& Generic != null && Generic.ArgumentTypes != null
&& (! object.ReferenceEquals(Generic.PrimaryType, this)))
{
return Generic.ArgumentTypes[i];
}
return argTypes[i];
}
}
}
}
if (javaType is java.lang.Class plainClass)
{
// a java generic Type may also be a java.lang.Class
return GetType(plainClass);
}
// if this is a java concept like GenericArrayType or WildcardType,
// or anything else we don't recognize, return System.Object
return ObjectType;
}
//
// UnboxJavaReturnValue
//
public static object UnboxJavaReturnValue(object value)
{
switch (value)
{
case java.lang.Boolean boolBox:
return system.Boolean.Box(boolBox.booleanValue() ? 1 : 0);
case java.lang.Byte byteBox:
return system.SByte.Box(byteBox.byteValue());
case java.lang.Character charBox:
return system.Char.Box(charBox.charValue());
case java.lang.Short shortBox:
return system.Int16.Box(shortBox.shortValue());
case java.lang.Integer intBox:
return system.Int32.Box(intBox.intValue());
case java.lang.Long longBox:
return system.Int64.Box(longBox.longValue());
case java.lang.Float floatBox:
return system.Single.Box(floatBox.floatValue());
case java.lang.Double doubleBox:
return system.Double.Box(doubleBox.doubleValue());
}
return value;
}
}
}

View File

@ -5,10 +5,7 @@ namespace system.resources
public class ResourceManager public class ResourceManager
{ {
public ResourceManager(string baseName, System.Reflection.Assembly assembly) public ResourceManager(string baseName, System.Reflection.Assembly assembly) { }
{
Console.WriteLine($"Creating ResourceManager with base name '{baseName}' in assembly '{assembly}'");
}
public string GetString(string name, System.Globalization.CultureInfo culture) => name; public string GetString(string name, System.Globalization.CultureInfo culture) => name;

View File

@ -7,8 +7,15 @@ using System.Runtime.Serialization;
namespace system namespace system
{ {
//
// System/RuntimeType.cs (this file) contains methods required
// by the type system. System/Reflection/RuntimeType2.cs
// contains methods to provide run-time reflection support.
//
[Serializable] [Serializable]
public class RuntimeType : system.reflection.TypeInfo, ISerializable, ICloneable public partial class RuntimeType : system.reflection.TypeInfo,
ISerializable, ICloneable
{ {
private sealed class GenericData private sealed class GenericData
@ -89,6 +96,26 @@ namespace system
break; break;
} }
} }
if (searchClass == javaClass)
{
// if Android 'R8' (ProGuard) stripped the InnerClass
// attribute, then the above getDeclaredClasses() would
// return nothing. in this case we use an alternative
// approach of scanning for the -generic-info-class
// field; see also TypeBuilder.BuildJavaClass()
foreach (var field in javaClass.getDeclaredFields())
{
// search for field: public static final synthetic,
// with a type that is public final synthetic
if ( field.getModifiers() == 0x1019
&& field.getType().getModifiers() == 0x1011)
{
searchClass = field.getType();
break;
}
}
}
} }
#pragma warning disable 0436 #pragma warning disable 0436
@ -307,16 +334,17 @@ namespace system
if (! TypeSystemInitialized) if (! TypeSystemInitialized)
{ {
Console.WriteLine("\n!!! Exception occured during initialization of the type system:\n"); java.lang.System.@out.println("\n!!! Exception occured during initialization of the type system:\n");
var exc = exception; var exc = exception;
for (;;) for (;;)
{ {
Console.WriteLine("Exception " + ((java.lang.Object) (object) exc).getClass() java.lang.System.@out.println(
+ "\nMessage: " + ((java.lang.Throwable) exc).getMessage() "Exception " + ((java.lang.Object) (object) exc).getClass()
+ "\n" + exc.StackTrace); + "\nMessage: " + ((java.lang.Throwable) exc).getMessage()
+ "\n" + exc.StackTrace);
if ((exc = exc.InnerException) == null) if ((exc = exc.InnerException) == null)
break; break;
Console.Write("Caused by Inner "); java.lang.System.@out.print("Caused by Inner ");
} }
} }
return exception; return exception;
@ -419,203 +447,8 @@ namespace system
public static bool op_Inequality(RuntimeType obj1, RuntimeType obj2) public static bool op_Inequality(RuntimeType obj1, RuntimeType obj2)
=> ! object.ReferenceEquals(obj1, obj2); => ! object.ReferenceEquals(obj1, obj2);
//
// MemberInfo
//
public override string Name
{
get
{
var generic = Generic;
if (JavaClass == null)
return GenericParameterName(generic);
if (JavaClass.isArray())
return (GetType(JavaClass.getComponentType()).Name) + "[]";
else
return ClassName(generic);
string ClassName(GenericData generic)
{
var name = JavaClass.getSimpleName();
int numArgsInDeclType = 0;
var declType = DeclaringType;
if (declType is RuntimeType declRuntimeType && declRuntimeType.Generic != null)
{
// a nested generic type duplicates the parameters of the parent
numArgsInDeclType = declRuntimeType.Generic.ArgumentTypes.Length;
}
if (generic != null)
{
int numArgs = generic.ArgumentTypes.Length;
int index = name.LastIndexOf("$$" + numArgs);
if (index != -1)
name = name.Substring(0, index);
numArgs -= numArgsInDeclType;
if (numArgs > 0)
name += "`" + numArgs.ToString();
}
return name;
}
string GenericParameterName(GenericData generic)
{
if (generic != null)
{
// generic parameter
var argTypes = generic.PrimaryType.Generic.ArgumentTypes;
for (int i = 0; i < argTypes.Length; i++)
{
if (object.ReferenceEquals(argTypes[i], this))
{
var typeNames = generic.PrimaryType.JavaClass.getTypeParameters();
if (i < typeNames.Length)
{
// if we have a valid generic signature, extract parameter name
var nm = typeNames[i].getName();
if (! string.IsNullOrEmpty(nm))
return nm;
}
return "T" + i.ToString();
}
}
}
return null; // invalid combination
}
}
}
public override Type DeclaringType
{
get
{
var javaClass = JavaClass;
if (javaClass == null)
return null;
if (javaClass.isArray())
javaClass = javaClass.getComponentType();
var declClass = javaClass.getDeclaringClass();
return (declClass == null) ? null : GetType(declClass);
}
}
public override Type ReflectedType => DeclaringType;
public override object[] GetCustomAttributes(bool inherit) => null;
public override object[] GetCustomAttributes(Type attributeType, bool inherit) => null;
public override bool IsDefined(Type attributeType, bool inherit) => false;
//
// _Type
//
public override Type BaseType
{
get
{
if ( JavaClass == null
|| object.ReferenceEquals(this, ObjectType)
|| IsInterface(this))
{
return null;
}
if (object.ReferenceEquals(this, ExceptionType))
{
// CilType translates System.Exception to java.lang.Throwable,
// and here we map java.lang.Throwable to system.Exception,
// thereby creating an infinite loop in which system.Exception
// inherits from java.lang.Exception, which inherits from
// java.lang.Throwable, which is mapped to system.Exception.
// the workaround breaks the infinite loop.
return ObjectType;
}
return FromJavaGenericType(JavaClass.getGenericSuperclass());
}
}
public override string Namespace
{
get
{
var javaClass = JavaClass;
if (javaClass == null)
return null;
if (javaClass.isArray())
{
do
{
javaClass = javaClass.getComponentType();
}
while (javaClass.isArray());
return GetType(javaClass).Namespace;
}
string dottedName = javaClass.getName();
int lastIndex = dottedName.LastIndexOf('.');
if (lastIndex <= 0)
return null;
int firstIndex = 0;
string resultName = "";
for (;;)
{
int nextIndex = dottedName.IndexOf('.', firstIndex);
var component = Char.ToUpperInvariant(dottedName[firstIndex])
+ dottedName.Substring(firstIndex + 1, nextIndex - firstIndex - 1);
resultName += component;
if (nextIndex == lastIndex)
return resultName;
resultName += ".";
firstIndex = nextIndex + 1;
}
}
}
public override string FullName
{
get
{
string name;
var declType = DeclaringType;
if (! object.ReferenceEquals(declType, null))
name = declType.FullName + "+";
else if (IsGenericParameter)
return null;
else
{
name = Namespace;
if (name != null)
name += ".";
}
name += Name;
return name;
}
}
public override Assembly Assembly => system.reflection.RuntimeAssembly.CurrentAssembly;
public override string AssemblyQualifiedName
{
get
{
var name = FullName;
if (name != null)
name = Assembly.CreateQualifiedName(Assembly.FullName, name);
return name;
}
}
public override Module Module => Assembly.GetModules(false)[0];
public override System.Guid GUID => System.Guid.Empty; public override System.Guid GUID => System.Guid.Empty;
public override Type UnderlyingSystemType => this; public override Type UnderlyingSystemType => this;
// Attributes property // Attributes property
@ -665,44 +498,6 @@ namespace system
public override bool IsSerializable public override bool IsSerializable
=> ((GetAttributeFlagsImpl() & TypeAttributes.Serializable) != 0); => ((GetAttributeFlagsImpl() & TypeAttributes.Serializable) != 0);
public override Type[] GetInterfaces()
{
var output = new java.util.HashSet();
GetInterfacesInternal(JavaClass, output);
return (Type[]) output.toArray(System.Type.EmptyTypes);
void GetInterfacesInternal(java.lang.Class input, java.util.HashSet output)
{
foreach (var javaType in input.getGenericInterfaces())
{
output.add(FromJavaGenericType(javaType));
if (javaType is java.lang.Class classType)
GetInterfacesInternal(classType, output);
}
}
}
public override Type GetInterface(string name, bool ignoreCase)
{
if (name == null)
throw new ArgumentNullException();
StringComparison cmp = ignoreCase
? System.StringComparison.InvariantCultureIgnoreCase
: System.StringComparison.InvariantCulture;
foreach (var ifc in GetInterfaces())
{
if (string.Compare(name, ifc.FullName, cmp) == 0)
return ifc;
}
return null;
}
protected override bool IsCOMObjectImpl() => false; protected override bool IsCOMObjectImpl() => false;
protected override bool IsByRefImpl() => false; // always false? protected override bool IsByRefImpl() => false; // always false?
@ -744,16 +539,6 @@ namespace system
} }
} }
public override Type[] GetNestedTypes(BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override Type GetNestedType(string name, BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override Type[] GetGenericArguments() public override Type[] GetGenericArguments()
{ {
if (Generic != null) if (Generic != null)
@ -761,7 +546,7 @@ namespace system
return (Type[]) java.util.Arrays.copyOf( return (Type[]) java.util.Arrays.copyOf(
Generic.ArgumentTypes, Generic.ArgumentTypes.Length); Generic.ArgumentTypes, Generic.ArgumentTypes.Length);
} }
return new Type[0]; return EmptyTypeArray;
} }
public override Type GetGenericTypeDefinition() public override Type GetGenericTypeDefinition()
@ -798,8 +583,6 @@ namespace system
// Primitives types // Primitives types
// //
protected override TypeCode GetTypeCodeImpl() protected override TypeCode GetTypeCodeImpl()
=> (TypeCode) (((int) (CachedAttrs & AttrTypeCode) >> 24) + 1); => (TypeCode) (((int) (CachedAttrs & AttrTypeCode) >> 24) + 1);
@ -905,7 +688,7 @@ namespace system
public object CallConstructor(bool publicOnly) public object CallConstructor(bool publicOnly)
{ {
object[] args = (Generic == null) ? new object[0] : Generic.ArgumentTypes; object[] args = (Generic == null) ? EmptyObjectArray : Generic.ArgumentTypes;
try try
{ {
#pragma warning disable 0436 #pragma warning disable 0436
@ -1159,83 +942,10 @@ namespace system
} }
//
// reflection support - extract information from Java generic signatures
// (generated by MakeGenericSignature in GenericUtil)
//
Type FromJavaGenericType(java.lang.reflect.Type javaType)
{
if (javaType is java.lang.reflect.ParameterizedType parameterizedType)
{
// a ParameterizedType is GenericType<GenericArg1,...GenericArgN>
// where each GenericArg would be a TypeVariable or a Class
var primaryClass = (java.lang.Class) parameterizedType.getRawType();
var javaTypeArgs = parameterizedType.getActualTypeArguments();
int n = javaTypeArgs.Length;
var typeArgs = new Type[n];
for (int i = 0; i < n; i++)
typeArgs[i] = FromJavaGenericType(javaTypeArgs[i]);
return GetType(primaryClass, typeArgs);
}
if ( javaType is java.lang.reflect.TypeVariable varType
&& varType.getGenericDeclaration() is java.lang.Class varClass)
{
// a TypeVariable is a GenericArg with a reference to the type
// it belongs to. if that type (in the java generics mechanism)
// corresponds to 'this' type, and if 'this' type is a concrete
// generic instance (as opposed to a generic definition), then
// we grab a concrete generic type from 'this' type instance.
//
// for example, with A<T1,T2> and B<T1,T2> : A<T2,T1>, then
// for generic instance B<int,bool>, we are able to resolve
// the base type as A<bool,int>.
var varName = varType.getName();
var primaryType = GetType(varClass) as RuntimeType;
var argTypes = primaryType?.Generic?.ArgumentTypes;
if (argTypes != null)
{
int n = argTypes.Length;
for (int i = 0; i < n; i++)
{
if (argTypes[i].Name == varName)
{
if ( varClass == JavaClass
&& Generic != null && Generic.ArgumentTypes != null
&& (! object.ReferenceEquals(Generic.PrimaryType, this)))
{
return Generic.ArgumentTypes[i];
}
return argTypes[i];
}
}
}
}
if (javaType is java.lang.Class plainClass)
{
// a java generic Type may also be a java.lang.Class
return GetType(plainClass);
}
// if this is a java concept like GenericArrayType or WildcardType,
// or anything else we don't recognize, return System.Object
return ObjectType;
}
// //
// Methods to create and retrieve type objects // Methods to create and retrieve type objects
// //
public static Type GetType(java.lang.Class cls, Type[] argTypes) public static Type GetType(java.lang.Class cls, Type[] argTypes)
{ {
var key = new TypeKey(cls, argTypes); var key = new TypeKey(cls, argTypes);
@ -1376,6 +1086,9 @@ namespace system
if (TypeCache == null) if (TypeCache == null)
{ {
EmptyObjectArray = new object[0];
EmptyTypeArray = new Type[0];
TypeCache = new java.util.concurrent.ConcurrentHashMap(); TypeCache = new java.util.concurrent.ConcurrentHashMap();
TypeLock = new java.util.concurrent.locks.ReentrantLock(); TypeLock = new java.util.concurrent.locks.ReentrantLock();
@ -1464,6 +1177,9 @@ namespace system
[java.attr.RetainType] public static object[] EmptyObjectArray;
[java.attr.RetainType] public static Type[] EmptyTypeArray;
[java.attr.RetainType] private static java.util.concurrent.ConcurrentHashMap TypeCache; [java.attr.RetainType] private static java.util.concurrent.ConcurrentHashMap TypeCache;
[java.attr.RetainType] private static java.util.concurrent.locks.ReentrantLock TypeLock; [java.attr.RetainType] private static java.util.concurrent.locks.ReentrantLock TypeLock;
[java.attr.RetainType] private static java.lang.Class ValueTypeClass; [java.attr.RetainType] private static java.lang.Class ValueTypeClass;
@ -1672,85 +1388,6 @@ namespace system
} }
//
// Reflection on members of the type
//
public override MethodBase DeclaringMethod
=> throw new PlatformNotSupportedException();
protected override MethodInfo GetMethodImpl(
string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
Type[] types, ParameterModifier[] modifiers)
{
if (JavaClass == null) // if generic parameter
return null;
return system.reflection.RuntimeMethodInfo.GetMethod(
name, bindingAttr, binder, callConvention, types, modifiers, this);
}
protected override ConstructorInfo GetConstructorImpl(
BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
Type[] types, ParameterModifier[] modifiers)
{
if (JavaClass == null) // if generic parameter
return null;
return system.reflection.RuntimeConstructorInfo.GetConstructor(
bindingAttr, binder, callConvention, types, modifiers, this);
}
public override object InvokeMember(
string name, BindingFlags invokeAttr, Binder binder, object target, object[] args,
ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
{
throw new PlatformNotSupportedException();
}
protected override PropertyInfo GetPropertyImpl(
string name, BindingFlags bindingAttr, Binder binder, Type returnType,
Type[] types, ParameterModifier[] modifiers)
{
throw new PlatformNotSupportedException();
}
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return system.reflection.RuntimeFieldInfo.GetFields(bindingAttr, this);
}
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
{
throw new PlatformNotSupportedException();
}
public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
public override EventInfo[] GetEvents(BindingFlags bindingAttr)
=> throw new PlatformNotSupportedException();
// //
// unimplemented methods from System.Type: // unimplemented methods from System.Type:
// base implementations throw NotSupportedException // base implementations throw NotSupportedException

View File

@ -143,6 +143,13 @@ namespace system.text
return this; return this;
} }
public StringBuilder Append(object value)
{
if (value != null)
sb.append(value.ToString());
return this;
}
public int Capacity public int Capacity
{ {
get => sb.capacity(); get => sb.capacity();

View File

@ -171,6 +171,18 @@ namespace SpaceFlint.CilToJava
public List<CilType> Parameters; public List<CilType> Parameters;
public CilType ReturnType; public CilType ReturnType;
// any resolved generic types from return type and parameters,
// concatenated together. used to differentiate generic methods
// that differ only in return or parameters types. for example:
//
// interface I1<T> { int M(T v); }
// interface I2<T> { int M(T v); }
// class CC<T1,T2> : I1<T2>, I2<T1>
// int M(T1 v) => ...;
// int M(T2 v) => ...;
//
// see also InterfaceBuilder.BuildGenericProxy()
public string ResolvedGenericTypes;
public CilInterfaceMethod(CilMethod fromMethod) public CilInterfaceMethod(CilMethod fromMethod)
@ -186,11 +198,14 @@ namespace SpaceFlint.CilToJava
if (idx != -1) if (idx != -1)
name = name.Substring(idx + 1); name = name.Substring(idx + 1);
ResolvedGenericTypes = "";
var returnType = (CilType) fromMethod.ReturnType; var returnType = (CilType) fromMethod.ReturnType;
if (returnType.IsGenericParameter) if (returnType.IsGenericParameter)
{ {
var genericMark = CilMain.GenericStack.Mark(); var genericMark = CilMain.GenericStack.Mark();
var (type, index) = CilMain.GenericStack.Resolve(returnType.JavaName); var (type, index) = CilMain.GenericStack.Resolve(returnType.JavaName);
ResolvedGenericTypes = $"{type},{index};";
if (index == 0) if (index == 0)
returnType = type; returnType = type;
@ -208,6 +223,7 @@ namespace SpaceFlint.CilToJava
var genericMark = CilMain.GenericStack.Mark(); var genericMark = CilMain.GenericStack.Mark();
var (type, index) = CilMain.GenericStack.Resolve(parameter.JavaName); var (type, index) = CilMain.GenericStack.Resolve(parameter.JavaName);
ResolvedGenericTypes += $"{type},{index};";
if (index == 0) if (index == 0)
{ {
if (parameter.ArrayRank != 0) if (parameter.ArrayRank != 0)
@ -342,6 +358,9 @@ namespace SpaceFlint.CilToJava
continue; continue;
} }
if (fromMethod.HasCustomAttribute("Discard"))
continue; // skip if decorated with [java.attr.Discard]
var genericMark = CilMain.GenericStack.Mark(); var genericMark = CilMain.GenericStack.Mark();
var inputMethod = CilMain.GenericStack.EnterMethod(fromMethod); var inputMethod = CilMain.GenericStack.EnterMethod(fromMethod);

View File

@ -67,7 +67,8 @@ namespace SpaceFlint.CilToJava
internal static JavaClass CreateInnerClass(JavaClass outerClass, string innerName, internal static JavaClass CreateInnerClass(JavaClass outerClass, string innerName,
JavaAccessFlags innerFlags = 0) JavaAccessFlags innerFlags = 0,
bool markGenericEntity = false)
{ {
if (innerFlags == 0) if (innerFlags == 0)
{ {
@ -85,6 +86,9 @@ namespace SpaceFlint.CilToJava
innerClass.Fields = new List<JavaField>(); innerClass.Fields = new List<JavaField>();
innerClass.Methods = new List<JavaMethod>(); innerClass.Methods = new List<JavaMethod>();
if (markGenericEntity)
innerClass.AddInterface("system.IGenericEntity");
outerClass.AddInnerClass(innerClass); outerClass.AddInnerClass(innerClass);
return innerClass; return innerClass;
} }

View File

@ -80,7 +80,7 @@ namespace SpaceFlint.CilToJava
Flags |= CONSTRUCTOR; Flags |= CONSTRUCTOR;
Flags |= THIS_ARG | ARRAY_CALL; Flags |= THIS_ARG | ARRAY_CALL;
ImportParameters(fromMethod); ImportParameters(fromMethod, null);
ImportGenericParameters(fromMethod); ImportGenericParameters(fromMethod);
} }
@ -91,7 +91,7 @@ namespace SpaceFlint.CilToJava
DeclType = CilType.From(fromMethod.DeclaringType); DeclType = CilType.From(fromMethod.DeclaringType);
SetMethodType(defMethod); SetMethodType(defMethod);
bool appendSuffix = ImportParameters(fromMethod); bool appendSuffix = ImportParameters(fromMethod, defMethod);
TranslateNameClrToJvm(fromMethod, appendSuffix); TranslateNameClrToJvm(fromMethod, appendSuffix);
if (IsConstructor) if (IsConstructor)
@ -166,7 +166,7 @@ namespace SpaceFlint.CilToJava
bool ImportParameters(MethodReference fromMethod) bool ImportParameters(MethodReference fromMethod, MethodDefinition defMethod)
{ {
bool appendSuffix = false; bool appendSuffix = false;
@ -194,7 +194,10 @@ namespace SpaceFlint.CilToJava
nm += "array-" + paramType.ArrayRank + "-"; nm += "array-" + paramType.ArrayRank + "-";
if (paramType.IsByReference) if (paramType.IsByReference)
nm = "-ref" + nm.Replace("&", ""); nm = "-ref" + nm.Replace("&", "");
nm += "$" + ((GenericParameter) fromParameterType.GetElementType()).Position; nm += "$" + GenericParameterPosition(defMethod, i,
((GenericParameter)
fromParameterType.GetElementType()));
//nm += "$" + ((GenericParameter) fromParameterType.GetElementType()).Position;
paramType = CilType.WrapMethodGenericParameter(paramType, nm); paramType = CilType.WrapMethodGenericParameter(paramType, nm);
Flags |= GEN_ARGS; Flags |= GEN_ARGS;
} }
@ -275,6 +278,70 @@ namespace SpaceFlint.CilToJava
return true; return true;
} }
static int GenericParameterPosition(MethodDefinition defMethod,
int parameterIndex,
GenericParameter parameter)
{
// when a method has parameters with a generic type, the
// method name gets a suffix like -generic-$n, where n is
// the index of the generic parameter in the generic type.
// e.g., method "int Mth(TY)" in class CA<TX,TY> would be
// named "Mth-generic-$1" because TY has generic index 1.
//
// however when the method overrides a base class method,
// the n should be the index of the generic parameter in
// the base type. e.g., "override int Mth(UZ)" in class
// CB<UX,UY,UZ> : CA<UY,UZ> translated to "Mth-generic-$2"
// (as UZ has generic index 2 in CB) would be incorrect,
// because it breaks the overload/override chain.
//
// the code below identifies that CB.Mth is an override
// of a generic base type method, and that CB.UZ maps to
// CA.TY in the base Mth(), and prefers the index of CA.TY
// (i.e. 1) over CB.UZ (i.e. 2), to correctly translate
// the override as "Mth-generic-$1".
if ( parameter.Type == GenericParameterType.Type
&& defMethod != null
&& defMethod.IsVirtual && (! defMethod.IsNewSlot))
{
var thisClass = defMethod.DeclaringType;
if (thisClass.BaseType is GenericInstanceType baseClass)
{
var baseGenericArgs = baseClass.GenericArguments;
foreach (var baseMethod in CilType.AsDefinition(baseClass).Methods)
{
if ( baseMethod.IsVirtual
&& CompareMethods(defMethod, baseMethod))
{
// we found a base class method that matches
// the overriding method, so take the position
// (i.e. the generic parameter index within
// generic type) from the base method parameter
if (baseMethod.Parameters[parameterIndex].ParameterType
.GetElementType() is GenericParameter baseParameter
&& baseGenericArgs.Count > baseParameter.Position
&& baseGenericArgs[baseParameter.Position] == parameter)
{
return baseParameter.Position;
}
}
}
}
}
// in any other case, for example if this is not an
// overriding method, or if the base type is not generic,
// then select the position within the current type
return parameter.Position;
}
} }
@ -485,9 +552,25 @@ namespace SpaceFlint.CilToJava
{ {
var prefixType = MethodIsShadowing(defMethod); var prefixType = MethodIsShadowing(defMethod);
if (prefixType == null && DeclType.IsInterface && (! DeclType.IsRetainName)) if (prefixType == null && (! DeclType.IsRetainName))
{ {
prefixType = DeclType; if (DeclType.IsInterface)
{
prefixType = DeclType;
}
else
{
// if a class inherits from system.IDisposable, and
// defines a non-RetainName close() method, then we
// must rename the method to prevent collision with
// java.lang.AutoCloseable.close().
// see also ConvertInterfaceCall() in CodeCall module.
if ( defMethod.Name == "close" && (! IsRetainName)
&& DeclType.AssignableTo(SystemIDisposable))
{
prefixType = DeclType;
}
}
} }
if (prefixType != null) if (prefixType != null)
@ -876,5 +959,8 @@ namespace SpaceFlint.CilToJava
internal static readonly JavaMethodRef ValueClone = internal static readonly JavaMethodRef ValueClone =
new JavaMethod("system-ValueMethod-Clone", CilType.SystemValueType); new JavaMethod("system-ValueMethod-Clone", CilType.SystemValueType);
internal static readonly JavaType SystemIDisposable =
new JavaType(0, 0, "system.IDisposable");
} }
} }

View File

@ -28,6 +28,7 @@ namespace SpaceFlint.CilToJava
internal JavaStackMap stackMap; internal JavaStackMap stackMap;
internal int lineNumber; internal int lineNumber;
internal int numCastableInterfaces;
@ -51,8 +52,9 @@ namespace SpaceFlint.CilToJava
o.defMethodBody = defMethod.Body; o.defMethodBody = defMethod.Body;
o.newMethod = newMethod; o.newMethod = newMethod;
o.method = myMethod; o.method = myMethod;
o.numCastableInterfaces = numCastableInterfaces;
o.Process(numCastableInterfaces); o.Process();
} }
catch (Exception e) catch (Exception e)
{ {
@ -77,7 +79,7 @@ namespace SpaceFlint.CilToJava
void Process(int numCastableInterfaces) void Process()
{ {
code = newMethod.Code = new JavaCode(newMethod); code = newMethod.Code = new JavaCode(newMethod);
var oldLabel = code.SetLabel(0xFFFF); var oldLabel = code.SetLabel(0xFFFF);
@ -86,7 +88,7 @@ namespace SpaceFlint.CilToJava
arrays = new CodeArrays(code, locals); arrays = new CodeArrays(code, locals);
exceptions = new CodeExceptions(defMethodBody, code, locals); exceptions = new CodeExceptions(defMethodBody, code, locals);
InsertMethodInitCode(numCastableInterfaces); InsertMethodInitCode();
code.SetLabel(oldLabel); code.SetLabel(oldLabel);
@ -101,7 +103,7 @@ namespace SpaceFlint.CilToJava
void InsertMethodInitCode(int numCastableInterfaces) void InsertMethodInitCode()
{ {
if (method.IsStatic) if (method.IsStatic)
{ {
@ -135,7 +137,7 @@ namespace SpaceFlint.CilToJava
// init the array of generic interfaces // init the array of generic interfaces
InterfaceBuilder.InitInterfaceArrayField( InterfaceBuilder.InitInterfaceArrayField(
method.DeclType, numCastableInterfaces, code); method.DeclType, numCastableInterfaces, code, 0);
// in any constructor, we want to allocate boxed instance fields // in any constructor, we want to allocate boxed instance fields
ValueUtil.InitializeInstanceFields(newMethod.Class, method.DeclType, ValueUtil.InitializeInstanceFields(newMethod.Class, method.DeclType,

View File

@ -180,6 +180,7 @@ namespace SpaceFlint.CilToJava
var currentClass = method.DeclType; var currentClass = method.DeclType;
var callClass = callMethod.DeclType; var callClass = callMethod.DeclType;
bool isCallToClone = false;
byte op; byte op;
if (callMethod.IsStatic) if (callMethod.IsStatic)
{ {
@ -245,10 +246,7 @@ namespace SpaceFlint.CilToJava
} }
if (callMethod.Name == "clone" && callMethod.Parameters.Count == 0) if (callMethod.Name == "clone" && callMethod.Parameters.Count == 0)
{ isCallToClone = true;
// if calling clone on the super object, implement Cloneable
code.Method.Class.AddInterface("java.lang.Cloneable");
}
} }
else else
{ {
@ -272,6 +270,22 @@ namespace SpaceFlint.CilToJava
ClearMethodArguments(callMethod, (op == 0xB7)); ClearMethodArguments(callMethod, (op == 0xB7));
PushMethodReturnType(callMethod); PushMethodReturnType(callMethod);
if (isCallToClone)
{
// if calling clone on the super object, implement Cloneable
code.Method.Class.AddInterface("java.lang.Cloneable");
if (numCastableInterfaces != 0)
{
code.NewInstruction(
0xC0 /* checkcast */, method.DeclType, null);
// init the array of generic interfaces
InterfaceBuilder.InitInterfaceArrayField(
method.DeclType, numCastableInterfaces, code, -1);
}
}
} }
return true; return true;
@ -306,7 +320,12 @@ namespace SpaceFlint.CilToJava
callClass = CilType.From(CilType.SystemValueType); callClass = CilType.From(CilType.SystemValueType);
} }
else else
{
if (ConvertInterfaceCall(callClass, callMethod))
return true;
op = 0xB9; // invokeinterface op = 0xB9; // invokeinterface
}
} }
else else
{ {
@ -468,6 +487,40 @@ namespace SpaceFlint.CilToJava
bool ConvertInterfaceCall(CilType callClass, CilMethod callMethod)
{
// convert a call to System.IDisposable.Dispose() to call
// java.lang.AutoCloseable.close(), so a "using" statement
// can reference java classes imported by DotNetImporter.
//
// note that our system.IDisposable in baselib inherits from
// java.lang.AutoCloseable and has a default implementation
// of close() that calls Dispatch().
//
// see also handling of java.lang.AutoCloseable in DotNetImporter,
// and of close() method in CilMethod.InsertMethodNamePrefix.
if ( callClass.ClassName == CilMethod.SystemIDisposable.ClassName
&& callMethod.Name == "system-IDisposable-Dispose"
&& callMethod.Parameters.Count == 0)
{
if (method.DeclType.ClassName == CilMethod.SystemIDisposable.ClassName)
{
// we are compiling system.IDisposable.close(),
// and want to keep the call to Dispose() as is
return false;
}
code.NewInstruction(0xB9 /* invokeinterface */,
new JavaType(0, 0, "java.lang.AutoCloseable"),
new JavaMethodRef("close", JavaType.VoidType));
ClearMethodArguments(callMethod, false);
return true;
}
return false;
}
bool Translate_Constrained(CilMethod callMethod, object data) bool Translate_Constrained(CilMethod callMethod, object data)
{ {
var typeRef = data as TypeReference; var typeRef = data as TypeReference;

View File

@ -763,6 +763,8 @@ namespace SpaceFlint.CilToJava
indexLocalVars1 += indexLocalVars0;*/ indexLocalVars1 += indexLocalVars0;*/
int resetLength = resetLocals.Length; int resetLength = resetLocals.Length;
bool canBreakEarly = true;
var inst = targetInst; var inst = targetInst;
while (inst != branchInst) while (inst != branchInst)
{ {
@ -794,6 +796,29 @@ namespace SpaceFlint.CilToJava
(ushort) (inst.Offset + 1), resetLocals); (ushort) (inst.Offset + 1), resetLocals);
} }
// if we hit a 'return' or 'throw', then we can (and should)
// break early, to minimize the area of effect, and the risk
// of breaking code between L.A and L.C. (consider that a
// .Net compiler may generate code between labels L.A and L.C
// that expects the local to be valid.)
var flowControl = inst.OpCode.FlowControl;
if ( flowControl == Mono.Cecil.Cil.FlowControl.Branch
|| flowControl == Mono.Cecil.Cil.FlowControl.Cond_Branch)
{
// indicate that we see non-sequential control flow
canBreakEarly = false;
}
else if ( canBreakEarly
&& ( flowControl == Mono.Cecil.Cil.FlowControl.Return
|| flowControl == Mono.Cecil.Cil.FlowControl.Throw))
{
// if we only saw sequential control flow, then we can
// break early as soon as we see a 'return' or a 'throw'
break;
}
inst = inst.Next; inst = inst.Next;
} }
} }

View File

@ -125,6 +125,10 @@ namespace SpaceFlint.CilToJava
byte op; byte op;
if (! isStatic) if (! isStatic)
{ {
if (method.IsConstructor &&
LoadFieldInConstructor(fldName, fldType, fldClass))
return true;
PopObjectAndLoadFromSpan(fldClass); PopObjectAndLoadFromSpan(fldClass);
op = 0xB4; // getfield op = 0xB4; // getfield
} }
@ -180,6 +184,108 @@ namespace SpaceFlint.CilToJava
bool LoadFieldInConstructor(string fldName, CilType fldType, CilType fldClass)
{
// Java does not allow 'getfield' instructions on an 'uninitializedThis'
// until the call to super constructor. but in .Net this is permitted,
// and the F# compiler generates such code in some cases. we try to work
// around this, by identifying the constructor parameter that was used in
// an earlier 'putfield', and loading that, instead of doing 'getfield'.
bool isUninitializedThisField =
( method.IsConstructor && fldClass.Equals(method.DeclType)
&& stackMap.GetLocal(0).Equals(JavaStackMap.UninitializedThis));
if (! isUninitializedThisField)
return false;
// most recent instruction before the 'getfield'
// should have been 'ldarg.0', translated to 'aload_0'
int i = code.Instructions.Count - 1;
if (i > 0 && code.Instructions[i].Opcode == 0x19 /* aload */
&& code.Instructions[i].Data is int thisIndex
&& thisIndex == 0
// note that we pop the stack here
&& code.StackMap.PopStack(CilMain.Where)
.Equals(JavaStackMap.UninitializedThis))
{
// try to find an earlier 'putfield' instruction for the field
while (i-- > 0)
{
var inst = code.Instructions[i];
if ( inst.Opcode == 0xB5 /* putfield */
&& method.DeclType.Equals(inst.Class))
{
var instField = (JavaFieldRef) inst.Data;
if ( fldName == instField.Name
&& fldType.Equals(instField.Type))
{
// try to find the load instruction that was used
// to load the value, for that earlier 'putfield'
if (FindLoadLocal(i - 1, code.Instructions.Count - 1))
return true;
}
}
}
}
throw new Exception("load from uninitialized this");
bool FindLoadLocal(int prevIdx, int lastIdx)
{
var prevInst = code.Instructions[prevIdx];
if ( prevIdx > 0 && prevInst.Opcode == 0xB8 /* invokestatic */
&& (JavaMethodRef) prevInst.Data is JavaMethodRef prevMethod)
{
if (prevMethod.Name == "Box")
{
// we possibly found the sequence used in boxing -
// xload value, invokestatic Value.Box(), putfield
prevInst = code.Instructions[prevIdx - 1];
}
else if ( prevIdx > 5 && prevMethod.Name == "Copy"
&& code.Instructions[prevIdx - 1].Opcode == 0x5A /* dup_x1 */
&& code.Instructions[prevIdx - 2].Opcode == 0xB8 /* invokestatic */
&& code.Instructions[prevIdx - 3].Opcode == 0x19 /* aload (type) */
&& code.Instructions[prevIdx - 4].Opcode == 0xB8 /* invokestatic */
&& code.Instructions[prevIdx - 5].Opcode == 0x19 /* aload (value) */)
{
// we possibly found the sequence used in generics -
// aload (local to use for store value)
// invokestatic Generic.Load
// aload (generic type parameter)
// invokestatic Generic.New
// dup_x1
// invokestatic Generic.Copy <=== prevIdx
// putfield
// (see also StoreInstance method in this module)
prevInst = code.Instructions[prevIdx - 5];
}
}
if ( prevInst.Opcode >= 0x15 /* iload, lload, fload, */
&& prevInst.Opcode <= 0x19 /* dload, aload */
&& prevInst.Data is int localIndex)
{
// if the instruction before the 'putfield' is a load
// from local, and assuming this local is a parameter,
// then we can just load this local again, to replace
// a 'getfield' instruction that cannot access 'this'
code.Instructions[lastIdx].Opcode = prevInst.Opcode;
code.Instructions[lastIdx].Data = localIndex;
stackMap.PushStack(code.StackMap.GetLocal(localIndex));
return true;
}
return false;
}
}
bool StoreFieldValue(string fldName, CilType fldType, CilType fldClass, bool StoreFieldValue(string fldName, CilType fldType, CilType fldClass,
bool isStatic, bool isVolatile) bool isStatic, bool isVolatile)
{ {

View File

@ -461,9 +461,10 @@ namespace SpaceFlint.CilToJava
} }
else else
{ {
if (localType.IsGenericParameter && (! localType.IsByReference)) if (localType.IsGenericParameter)
{ {
GenericUtil.ValueLoad(code); if (! localType.IsByReference)
GenericUtil.ValueLoad(code);
localType = CilType.From(JavaType.ObjectType); localType = CilType.From(JavaType.ObjectType);
} }
@ -612,7 +613,7 @@ namespace SpaceFlint.CilToJava
} }
/*
public List<(CilType, int)> GetUninitializedVariables() public List<(CilType, int)> GetUninitializedVariables()
{ {
List<(CilType, int)> list = null; List<(CilType, int)> list = null;
@ -629,7 +630,7 @@ namespace SpaceFlint.CilToJava
} }
return list; return list;
} }
*/
public (CilType, int) GetLocalFromLoadInst(Code op, object data) public (CilType, int) GetLocalFromLoadInst(Code op, object data)

View File

@ -346,13 +346,17 @@ namespace SpaceFlint.CilToJava
if (CodeSpan.LoadStore(false, intoType, null, dataType, code)) if (CodeSpan.LoadStore(false, intoType, null, dataType, code))
return; return;
if ( (! dataType.IsReference) if ( intoType is BoxedType intoBoxedType
&& intoType is BoxedType intoBoxedType && dataType.PrimitiveType ==
&& dataType.PrimitiveType == intoBoxedType.UnboxedType.PrimitiveType) intoBoxedType.UnboxedType.PrimitiveType)
{ {
// 'stobj primitive' with a primitive value on the stack // 'stobj primitive' with a primitive value on the stack,
intoBoxedType.SetValueOV(code); // or 'stobj primitive[]' with a primitive array on the stack
return; if ((! dataType.IsReference) || dataType.IsArray)
{
intoBoxedType.SetValueOV(code);
return;
}
} }
code.StackMap.PushStack(intoType); code.StackMap.PushStack(intoType);

View File

@ -178,7 +178,8 @@ namespace SpaceFlint.CilToJava
// //
JavaClass CreateClass(JavaClass fromClass) => JavaClass CreateClass(JavaClass fromClass) =>
CilMain.CreateInnerClass(fromClass, fromClass.Name + "$$static", 0); CilMain.CreateInnerClass(fromClass, fromClass.Name + "$$static", 0,
markGenericEntity: true);
// //
// create a private instance field to hold the runtime type // create a private instance field to hold the runtime type
@ -345,13 +346,14 @@ namespace SpaceFlint.CilToJava
} }
if (! anyVariance) if (! anyVariance)
{ {
/* removed; see IComparer.cs in baselib
if (fromType.JavaName == "system.collections.generic.IComparer$$1") if (fromType.JavaName == "system.collections.generic.IComparer$$1")
{ {
// force a variance string for an interface that we create // force a variance string for an interface that we create
// as an abstract class; see also IComparer.cs in baselib // as an abstract class; see also IComparer.cs in baselib
varianceString = "I"; varianceString = "I";
} }
else else*/
return; return;
} }
@ -487,6 +489,16 @@ namespace SpaceFlint.CilToJava
{ {
if (loadIndex < 0) if (loadIndex < 0)
{ {
if ( code.Method.Class != null
&& (code.Method.Class
.Flags & JavaAccessFlags.ACC_INTERFACE) != 0)
{
// a method compiled as part of an interface does not
// have access to the generic-type member field
throw CilMain.Where.Exception(
"unsupported generic argument reference in interface method");
}
// generic type is accessible through the generic-type member field // generic type is accessible through the generic-type member field
code.NewInstruction(0x19 /* aload */, null, (int) 0); code.NewInstruction(0x19 /* aload */, null, (int) 0);

View File

@ -74,7 +74,6 @@ namespace SpaceFlint.CilToJava
// the RuntimeType constructor in baselib uses IGenericEntity // the RuntimeType constructor in baselib uses IGenericEntity
// marker interface to identify generic classes. note that // marker interface to identify generic classes. note that
// real generic types implement IGenericObject -> IGenericEntity. // real generic types implement IGenericObject -> IGenericEntity.
theClass.AddInterface("system.IGenericEntity"); theClass.AddInterface("system.IGenericEntity");
} }
@ -124,14 +123,14 @@ namespace SpaceFlint.CilToJava
// if the class implements a generic interface for multiple types, // if the class implements a generic interface for multiple types,
// then we need a method suffix to differentiate between the methods. // then we need a method suffix to differentiate between the methods.
// see also: CilMethod::InsertMethodNamePrefix // see also: CilMethod::InsertMethodNamePrefix
string methodSuffix = ""; /*string methodSuffix = "";
foreach (var genericType in ifc.GenericTypes) foreach (var genericType in ifc.GenericTypes)
methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType); methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType);*/
foreach (var ifcMethod in ifc.Methods) foreach (var ifcMethod in ifc.Methods)
{ {
// build proxy classes: proxy sub-class -> this class // build proxy classes: proxy sub-class -> this class
BuildGenericProxy(ifcMethod, methodSuffix, intoType, theMethods, ifcClass); BuildGenericProxy(ifcMethod, /*methodSuffix,*/ intoType, theMethods, ifcClass);
} }
} }
} }
@ -147,7 +146,8 @@ namespace SpaceFlint.CilToJava
// reference as a parameter and initializes the instance field. // reference as a parameter and initializes the instance field.
var newClass = CilMain.CreateInnerClass(parentClass, var newClass = CilMain.CreateInnerClass(parentClass,
parentClass.Name + "$$generic" + ifcNumber.ToString()); parentClass.Name + "$$generic" + ifcNumber.ToString(),
markGenericEntity: true);
var fld = new JavaField(); var fld = new JavaField();
fld.Name = ParentFieldName; fld.Name = ParentFieldName;
@ -199,7 +199,7 @@ namespace SpaceFlint.CilToJava
// //
public static void InitInterfaceArrayField(CilType toType, int numCastableInterfaces, public static void InitInterfaceArrayField(CilType toType, int numCastableInterfaces,
JavaCode code) JavaCode code, int objectIndex)
{ {
// if a type has castable interface (as counted by CastableInterfaceCount), // if a type has castable interface (as counted by CastableInterfaceCount),
// then we need to initialize the helper array field, for use by the // then we need to initialize the helper array field, for use by the
@ -208,8 +208,14 @@ namespace SpaceFlint.CilToJava
if (numCastableInterfaces == 0) if (numCastableInterfaces == 0)
return; return;
code.NewInstruction(0x19 /* aload */, null, (int) 0); // objectIndex specifies the local index for the object reference,
// e.g. 0 for the 'this' object, or -1 for top of stack.
if (objectIndex == -1)
code.NewInstruction(0x59 /* dup */, null, null);
else
code.NewInstruction(0x19 /* aload */, null, objectIndex);
code.StackMap.PushStack(toType); code.StackMap.PushStack(toType);
code.NewInstruction(0xBB /* new */, AtomicReferenceArrayType, null); code.NewInstruction(0xBB /* new */, AtomicReferenceArrayType, null);
code.StackMap.PushStack(AtomicReferenceArrayType); code.StackMap.PushStack(AtomicReferenceArrayType);
code.NewInstruction(0x59 /* dup */, null, null); code.NewInstruction(0x59 /* dup */, null, null);
@ -381,7 +387,7 @@ namespace SpaceFlint.CilToJava
public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, string methodSuffix, public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, /*string methodSuffix,*/
CilType intoType, List<CilInterfaceMethod> classMethods, CilType intoType, List<CilInterfaceMethod> classMethods,
JavaClass ifcClass) JavaClass ifcClass)
{ {
@ -408,9 +414,23 @@ namespace SpaceFlint.CilToJava
{ {
// more than one method may match, if a derived type overrides // more than one method may match, if a derived type overrides
// or hides a method that also exists in a base type. but the // or hides a method that also exists in a base type. but the
// derived (primary) type methods always come first. // derived (primary) type methods always come first
if (targetMethod == null) if (targetMethod == null)
targetMethod = clsMethod.Method; targetMethod = clsMethod.Method;
// if a second method matches, and the set of generic types
// in its signature exactly matches the interface method we
// are looking for, then prefer this method. when a class
// implements same-name methods from multiple interfaces,
// this is needed to pick the right method.
// see also ResolvedGenericTypes in CilInterfaceMethod.
else if ( clsMethod.ResolvedGenericTypes.Length != 0
&& clsMethod.ResolvedGenericTypes
== ifcMethod.ResolvedGenericTypes)
{
targetMethod = clsMethod.Method;
}
} }
} }
} }

View File

@ -100,8 +100,24 @@ namespace SpaceFlint.CilToJava
// Android 'D8' desugars static methods on an interface by // Android 'D8' desugars static methods on an interface by
// moving into a separate class, so we do it ourselves. // moving into a separate class, so we do it ourselves.
// see also system.RuntimeType.CreateGeneric() in baselib // see also system.RuntimeType.CreateGeneric() in baselib
infoClass = CilMain.CreateInnerClass(jclass, jclass.Name + "$$info"); infoClass = CilMain.CreateInnerClass(jclass, jclass.Name + "$$info",
markGenericEntity: true);
CilMain.JavaClasses.Add(infoClass); CilMain.JavaClasses.Add(infoClass);
// Android 'R8' (ProGuard) might discard this new class,
// so insert a dummy field with the type of the class.
// see also ProGuard rules in IGenericEntity in baselib
var infoClassField = new JavaField();
infoClassField.Name = "-generic-info-class";
infoClassField.Type = new JavaType(0, 0, infoClass.Name);
infoClassField.Class = jclass;
infoClassField.Flags = JavaAccessFlags.ACC_PUBLIC
| JavaAccessFlags.ACC_STATIC
| JavaAccessFlags.ACC_FINAL
| JavaAccessFlags.ACC_SYNTHETIC;
if (jclass.Fields == null)
jclass.Fields = new List<JavaField>(1);
jclass.Fields.Add(infoClassField);
} }
GenericUtil.CreateGenericInfoMethod(infoClass, dataClass, myType); GenericUtil.CreateGenericInfoMethod(infoClass, dataClass, myType);
@ -347,10 +363,12 @@ namespace SpaceFlint.CilToJava
attrs &= ~FieldAttributes.Static; attrs &= ~FieldAttributes.Static;
} }
if ((attrs & FieldAttributes.InitOnly) != 0) if (0 != (attrs & ( FieldAttributes.InitOnly
| FieldAttributes.Literal)))
{ {
flags |= JavaAccessFlags.ACC_FINAL; flags |= JavaAccessFlags.ACC_FINAL;
attrs &= ~FieldAttributes.InitOnly; attrs &= ~( FieldAttributes.InitOnly
| FieldAttributes.Literal);
} }
if ((attrs & FieldAttributes.NotSerialized) != 0) if ((attrs & FieldAttributes.NotSerialized) != 0)
@ -359,8 +377,7 @@ namespace SpaceFlint.CilToJava
attrs &= ~FieldAttributes.NotSerialized; attrs &= ~FieldAttributes.NotSerialized;
} }
attrs &= ~( FieldAttributes.Literal attrs &= ~( FieldAttributes.HasFieldRVA
| FieldAttributes.HasFieldRVA
| FieldAttributes.HasDefault | FieldAttributes.HasDefault
| FieldAttributes.SpecialName | FieldAttributes.SpecialName
| FieldAttributes.RTSpecialName); | FieldAttributes.RTSpecialName);
@ -385,10 +402,8 @@ namespace SpaceFlint.CilToJava
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
var defMethod = cilType.Methods[i]; var defMethod = cilType.Methods[i];
/*
if (defMethod.HasCustomAttribute("Discard")) if (defMethod.HasCustomAttribute("Discard"))
continue; // if decorated with [java.attr.Discard], don't export to java continue; // if decorated with [java.attr.Discard], don't export to java
*/
var genericMark = CilMain.GenericStack.Mark(); var genericMark = CilMain.GenericStack.Mark();
var myMethod = CilMain.GenericStack.EnterMethod(defMethod); var myMethod = CilMain.GenericStack.EnterMethod(defMethod);

View File

@ -18,7 +18,7 @@ namespace SpaceFlint.CilToJava
valueClass.Super = CilType.SystemValueType.ClassName; valueClass.Super = CilType.SystemValueType.ClassName;
CreateDefaultConstructor(valueClass, fromType, numCastableInterfaces, true); CreateDefaultConstructor(valueClass, fromType, numCastableInterfaces, true);
CreateValueMethods(valueClass, fromType); CreateValueMethods(valueClass, fromType, numCastableInterfaces);
if ((valueClass.Flags & JavaAccessFlags.ACC_ABSTRACT) == 0) if ((valueClass.Flags & JavaAccessFlags.ACC_ABSTRACT) == 0)
valueClass.Flags |= JavaAccessFlags.ACC_FINAL; valueClass.Flags |= JavaAccessFlags.ACC_FINAL;
@ -58,7 +58,8 @@ namespace SpaceFlint.CilToJava
} }
// init the array of generic interfaces // init the array of generic interfaces
InterfaceBuilder.InitInterfaceArrayField(fromType, numCastableInterfaces, code); InterfaceBuilder.InitInterfaceArrayField(
fromType, numCastableInterfaces, code, 0);
if (initFields) if (initFields)
{ {
@ -82,11 +83,12 @@ namespace SpaceFlint.CilToJava
static void CreateValueMethods(JavaClass valueClass, CilType fromType) static void CreateValueMethods(JavaClass valueClass, CilType fromType,
int numCastableInterfaces)
{ {
CreateValueClearMethod(valueClass, fromType); CreateValueClearMethod(valueClass, fromType);
CreateValueCopyToMethod(valueClass, fromType); CreateValueCopyToMethod(valueClass, fromType);
CreateValueCloneMethod(valueClass, fromType); CreateValueCloneMethod(valueClass, fromType, numCastableInterfaces);
// //
// system-ValueMethod-Clear() resets all fields to their default value // system-ValueMethod-Clear() resets all fields to their default value
@ -180,9 +182,11 @@ namespace SpaceFlint.CilToJava
// any boxed fields // any boxed fields
// //
void CreateValueCloneMethod(JavaClass valueClass, CilType fromType) void CreateValueCloneMethod(JavaClass valueClass, CilType fromType,
int numCastableInterfaces)
{ {
var code = CilMain.CreateHelperMethod(valueClass, CilMethod.ValueClone, 1, 3); var code = CilMain.CreateHelperMethod(valueClass, CilMethod.ValueClone,
1, (numCastableInterfaces == 0) ? 3 : 5);
bool atLeastOneField = false; bool atLeastOneField = false;
code.NewInstruction(0x19 /* aload */, null, (int) 0); code.NewInstruction(0x19 /* aload */, null, (int) 0);
@ -215,7 +219,15 @@ namespace SpaceFlint.CilToJava
} }
if (! atLeastOneField) if (! atLeastOneField)
code.NewInstruction(0xC0 /* checkcast */, CilType.SystemValueType, null); code.NewInstruction(0xC0 /* checkcast */, fromType, null);
if (numCastableInterfaces != 0)
{
code.StackMap = new JavaStackMap();
// init the array of generic interfaces
InterfaceBuilder.InitInterfaceArrayField(
fromType, numCastableInterfaces, code, -1);
}
code.NewInstruction(fromType.ReturnOpcode, null, null); code.NewInstruction(fromType.ReturnOpcode, null, null);
} }

View File

@ -189,6 +189,8 @@ namespace SpaceFlint.JavaBinary
else if (inst.Data is double vDouble) else if (inst.Data is double vDouble)
{ {
if (FillInstruction_ConstLoad_Double(inst, vDouble))
return;
constantIndex = wtr.ConstDouble(vDouble); constantIndex = wtr.ConstDouble(vDouble);
op = 0x14; op = 0x14;
} }
@ -215,7 +217,11 @@ namespace SpaceFlint.JavaBinary
} }
else if (inst.Data is float vFloat) else if (inst.Data is float vFloat)
{
if (FillInstruction_ConstLoad_Float(inst, vFloat))
return;
constantIndex = wtr.ConstFloat(vFloat); constantIndex = wtr.ConstFloat(vFloat);
}
else if (inst.Data is string vString) else if (inst.Data is string vString)
constantIndex = wtr.ConstString(vString); constantIndex = wtr.ConstString(vString);
@ -284,6 +290,49 @@ namespace SpaceFlint.JavaBinary
bool FillInstruction_ConstLoad_Float(Instruction inst, float value)
{
if (value == 0.0f)
{
inst.Bytes = new byte[1];
inst.Bytes[0] = (byte) 11; // fconst_0
}
else if (value == 1.0f)
{
inst.Bytes = new byte[1];
inst.Bytes[0] = (byte) 12; // fconst_1
}
else if (value == 2.0f)
{
inst.Bytes = new byte[1];
inst.Bytes[0] = (byte) 13; // fconst_2
}
else
return false;
return true;
}
bool FillInstruction_ConstLoad_Double(Instruction inst, double value)
{
if (value == 0.0)
{
inst.Bytes = new byte[1];
inst.Bytes[0] = (byte) 14; // dconst_0
}
else if (value == 1.0)
{
inst.Bytes = new byte[1];
inst.Bytes[0] = (byte) 15; // dconst_1
}
else
return false;
return true;
}
bool FillInstruction_Local(JavaWriter wtr, Instruction inst, byte op) bool FillInstruction_Local(JavaWriter wtr, Instruction inst, byte op)
{ {
if (op == 0x84) if (op == 0x84)

View File

@ -589,7 +589,11 @@ namespace SpaceFlint.JavaBinary
offset = (ushort) (offset - (lastOffset + 1)); offset = (ushort) (offset - (lastOffset + 1));
item.deltaOffset = offset; item.deltaOffset = offset;
// discard any trailing 'top' elements for out-of-scape locals
int numLocals = frame.locals.Count; int numLocals = frame.locals.Count;
while (numLocals > 0 && frame.locals[numLocals - 1].Equals(Top))
numLocals--;
var localsList = new List<JavaAttribute.StackMapTable.Slot>(numLocals); var localsList = new List<JavaAttribute.StackMapTable.Slot>(numLocals);
for (int i = 0; i < numLocals; i += frame.locals[i].Category) for (int i = 0; i < numLocals; i += frame.locals[i].Category)
localsList.Add(JavaTypeToVerificationType(frame.locals[i], wtr)); localsList.Add(JavaTypeToVerificationType(frame.locals[i], wtr));

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2020 spaceflint7 Copyright (c) 2020, 2021 spaceflint7
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -182,6 +182,22 @@ public class DotNetImporter
} }
} }
// arrange Java inner classes as CIL nested classes,
// and add Java outer classes to the CIL module itself
foreach (var jclass in classes2)
{
Where.Push($"class '{jclass.Name}'");
var cilType = typeMap[jclass.Name] as TypeDefinition;
AddClassToDeclaringType(cilType, jclass);
Where.Pop();
}
// establish links between classes and their
// super classes and interfaces
foreach (var jclass in classes2) foreach (var jclass in classes2)
{ {
Where.Push($"class '{jclass.Name}'"); Where.Push($"class '{jclass.Name}'");
@ -335,7 +351,7 @@ public class DotNetImporter
public void LinkCilTypesByClass(TypeDefinition cilType, JavaClass jclass) public void AddClassToDeclaringType(TypeDefinition cilType, JavaClass jclass)
{ {
if (jclass.IsInnerClass()) if (jclass.IsInnerClass())
{ {
@ -363,7 +379,12 @@ public class DotNetImporter
{ {
module.Types.Add(cilType); module.Types.Add(cilType);
} }
}
public void LinkCilTypesByClass(TypeDefinition cilType, JavaClass jclass)
{
var superName = jclass.Super; var superName = jclass.Super;
if (superName == "java.lang.Enum") if (superName == "java.lang.Enum")
superName = null; superName = null;
@ -379,6 +400,16 @@ public class DotNetImporter
cilType.Interfaces.Add( cilType.Interfaces.Add(
new InterfaceImplementation(cilSuperTypeRef)); new InterfaceImplementation(cilSuperTypeRef));
} }
if (jclass.Name == "java.lang.AutoCloseable")
{
// make java.lang.AutoCloseable extend System.IDisposable,
// to allow use of imported classes in "using" statement.
// see also ConvertInterfaceCall in CodeCall module.
var iDisposableRef = new TypeReference(
"System", "IDisposable", module, module.TypeSystem.CoreLibrary);
cilType.Interfaces.Add(
new InterfaceImplementation(iDisposableRef));
}
} }
else else
{ {
@ -707,7 +738,7 @@ public class DotNetImporter
var method = new MethodDefinition( var method = new MethodDefinition(
"op_Explicit", attrs, CilTypeReference(JavaType.ClassType)); "op_Explicit", attrs, CilTypeReference(JavaType.ClassType));
method.Parameters.Add(new ParameterDefinition( method.Parameters.Add(new ParameterDefinition(
"type", 0, new TypeReference("System", "Type", "source", 0, new TypeReference("System", "Type",
module, module.TypeSystem.CoreLibrary))); module, module.TypeSystem.CoreLibrary)));
SetCommonMethodBody(method); SetCommonMethodBody(method);
cilType.Methods.Add(method); cilType.Methods.Add(method);

View File

@ -44,7 +44,7 @@ public class DotNetPrinter
{ {
txt.Write("{0}{1}", txt.Write("{0}{1}",
(comma ? ", " : string.Empty), (comma ? ", " : string.Empty),
intrface.InterfaceType.FullName); intrface.InterfaceType?.FullName ?? "(null)");
comma = true; comma = true;
} }

View File

@ -383,6 +383,20 @@ namespace Tests
{ {
Console.WriteLine("Caught exception " + e.GetType()); Console.WriteLine("Caught exception " + e.GetType());
} }
// test generic cast
var tupleArray = new Tuple<int,string>[3] {
new Tuple<int,string>(1, "one"),
new Tuple<int,string>(2, "two"),
new Tuple<int,string>(3, "three"),
};
var tupleEnum =
((System.Collections.Generic.IEnumerable<Tuple<int,string>>) tupleArray)
.GetEnumerator();
while (tupleEnum.MoveNext())
System.Console.Write(tupleEnum.Current);
System.Console.WriteLine();
} }
void TestValue(bool selector) void TestValue(bool selector)
@ -425,6 +439,21 @@ namespace Tests
list.Add(arr1); list.Add(arr1);
var arr2 = list.ToArray(); var arr2 = list.ToArray();
PrintArray(arr2[0]); PrintArray(arr2[0]);
var arr3 = new Guid[] {
new Guid("33333333333333333333333333333333"),
new Guid("22222222222222222222222222222222"),
new Guid("11111111111111111111111111111111"),
};
System.Array.Sort(arr3, new MyComparer<Guid>());
PrintArray(arr3);
}
public class MyComparer<T> : System.Collections.Generic.IComparer<T>
where T : System.IComparable<T>
{
public int Compare(T x, T y) => x.CompareTo(y);
object Clone() => MemberwiseClone();
} }
} }

View File

@ -24,6 +24,7 @@ namespace Tests
TestGenericInterface2(); TestGenericInterface2();
TestGenericOverload3(); TestGenericOverload3();
TestGenericMethodWithEnum(); TestGenericMethodWithEnum();
TestGenericByRef();
} }
// //
@ -179,7 +180,7 @@ namespace Tests
public virtual int Get(T2 v) => 2; public virtual int Get(T2 v) => 2;
} }
class MyOv2<T1,T2> : MyOv1<T1,T2> class MyOv2<T1,T2> : MyOv1<T2,T1>
{ {
public override int Get(T1 v) => 3; public override int Get(T1 v) => 3;
public override int Get(T2 v) => 4; public override int Get(T2 v) => 4;
@ -190,6 +191,8 @@ namespace Tests
var a = new MyOv1<int,bool>(); var a = new MyOv1<int,bool>();
Console.Write(a.Get(1)); Console.Write(a.Get(1));
Console.Write(a.Get(true)); Console.Write(a.Get(true));
Console.Write(((I1<bool>) a).Get(true));
Console.Write(((I2<int>) a).Get(1));
var b = new MyOv2<int,bool>(); var b = new MyOv2<int,bool>();
Console.Write(b.Get(1)); Console.Write(b.Get(1));
Console.WriteLine(b.Get(true)); Console.WriteLine(b.Get(true));
@ -224,6 +227,7 @@ namespace Tests
public class CccC1<T1> : CccB1<T1,int> { public override void DoIt(ref T1 a, ref int b) => Console.Write("OK1 "); } public class CccC1<T1> : CccB1<T1,int> { public override void DoIt(ref T1 a, ref int b) => Console.Write("OK1 "); }
public class CccC2<T1> : CccB1<T1,Version> { public override void DoIt(ref T1 a, ref Version b) => Console.Write("OK2 "); } public class CccC2<T1> : CccB1<T1,Version> { public override void DoIt(ref T1 a, ref Version b) => Console.Write("OK2 "); }
public class CccC3<T1> : CccB1<T1,object> { public override void DoIt(ref T1 a, ref object b) => Console.Write("OK3 "); } public class CccC3<T1> : CccB1<T1,object> { public override void DoIt(ref T1 a, ref object b) => Console.Write("OK3 "); }
public class CccC4<T0,T2,T1> : CccB1<T1,T2>{ public override void DoIt(ref T1 a, ref T2 b) => Console.Write("OK4 "); }
void TestGenericOverload3() void TestGenericOverload3()
{ {
@ -235,10 +239,12 @@ namespace Tests
CccB1<bool,int> c1 = new CccC1<bool>(); CccB1<bool,int> c1 = new CccC1<bool>();
CccB1<bool,Version> c2 = new CccC2<bool>(); CccB1<bool,Version> c2 = new CccC2<bool>();
CccB1<bool,object> c3 = new CccC3<bool>(); CccB1<bool,object> c3 = new CccC3<bool>();
CccB1<bool,object>c4 = new CccC4<int,object,bool>();
c1.DoIt(ref bFalse, ref iZero); c1.DoIt(ref bFalse, ref iZero);
c2.DoIt(ref bFalse, ref vZero); c2.DoIt(ref bFalse, ref vZero);
c3.DoIt(ref bFalse, ref oZero); c3.DoIt(ref bFalse, ref oZero);
c4.DoIt(ref bFalse, ref oZero);
Console.WriteLine(); Console.WriteLine();
} }
@ -258,5 +264,20 @@ namespace Tests
} }
} }
//
// TestGenericByRef
//
void TestGenericByRef()
{
var guid = new Guid("12345678123456781234567812345678");
Helper<Guid>(ref guid, true);
void Helper<T>(ref T arg, bool cond) where T : IFormattable
{
Console.WriteLine(arg.ToString(cond ? "" : "D", null));
}
}
} }
} }

View File

@ -98,6 +98,9 @@ namespace Tests
DisplayGenericType(tT, "Type parameter T from Base<T>"); DisplayGenericType(tT, "Type parameter T from Base<T>");
//DisplayGenericType(tF, "Field type, G<Derived<V>>"); //DisplayGenericType(tF, "Field type, G<Derived<V>>");
DisplayGenericType(tNested, "Nested type in Derived<V>"); DisplayGenericType(tNested, "Nested type in Derived<V>");
DisplayGenericType(tDerived.GetNestedType("Nested"),
"Nested type in Derived<V> (2)");
} }
public static void DisplayGenericType(Type t, string caption) public static void DisplayGenericType(Type t, string caption)

View File

@ -13,6 +13,7 @@ namespace Tests
{ {
TestSuppressGC(); TestSuppressGC();
TestUnhandledException(); TestUnhandledException();
TestUsing();
} }
// //
@ -46,6 +47,26 @@ namespace Tests
=> Console.WriteLine("In Unhandled Exception Handler"); => Console.WriteLine("In Unhandled Exception Handler");
} }
//
// TestUnhandledException
//
public class MyDisposable : System.IDisposable
{
public void Dispose() => Console.Write("Disposed ");
public void close() => Console.Write("Closing ");
}
void TestUsing()
{
using (var myDisposable = new MyDisposable())
{
myDisposable.Dispose();
myDisposable.close();
}
Console.WriteLine();
}
} }
} }

View File

@ -1,16 +1,16 @@
#### Goal #### Goal
- Set up development of an Android app using a .NET language - Set up development of an Android app using a .NET language.
- Either in Visual Studio or using the command line `dotnet` tool. - Either in Visual Studio or using the command line `dotnet` tool.
- Use Android Studio to build the app. - Use Android Studio to build the app.
- With a Gradle plugin to compile .NET code. - With a Gradle task to build the .NET project.
- And a Gradle build task to convert the .NET code to Java compiled form. - And a Gradle task to convert the .NET code to Java compiled form.
- Most of development should be possible on Windows without requiring an Android device. - Most of development should be possible on Windows without requiring an Android device.
@ -20,10 +20,10 @@
#### Environment Variables #### Environment Variables
- Set the environment variable `MSBUILD_EXE` to point to `MSBuild.exe` program file. - Set the environment variable `MSBUILD_EXE` to point to `MSBuild.exe` program file.
- For example, `C:\Program Files (x86)\Microsoft Visual Studio\\2019\Community\MSBuild\Current\Bin\MSBuild.exe` - For example, `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe`
- Set the environment variable `BLUEBONNET_DIR` to point to a directory containing `Bluebonnet.exe`, `PruneMerge.exe`, `Baselib.jar` and `Android.dll`. - Set the environment variable `BLUEBONNET_DIR` to point to a directory containing `Bluebonnet.exe`, `Baselib.jar` and `Android.dll`.
- These files can be downloaded from the [Bluebonnet releases](https://github.com/spaceflint7/bluebonnet/releases) page. - These files can be downloaded from the [Bluebonnet releases](https://github.com/spaceflint7/bluebonnet/releases) page.
@ -37,7 +37,7 @@
- You may use some other project type or language instead of a C# console app. - You may use some other project type or language instead of a C# console app.
- The project must be named `DotNet`. - The project should be named `DotNet`.
- Edit `DotNet.csproj` to add a reference to Android DLL created by Bluebonnet: - Edit `DotNet.csproj` to add a reference to Android DLL created by Bluebonnet:
@ -158,12 +158,13 @@
- At the top of the `dependencies` section, insert: - At the top of the `dependencies` section, insert:
implementation files("$buildDir/dotnet/dotnet.jar") implementation files("$buildDir/dotnet/dotnet.jar")
implementation files("${System.env.BLUEBONNET_DIR}/baselib.jar")
- After the `dependencies` section, append: - After the `dependencies` section, append:
task buildDotNet { task buildDotNet {
doLast { doLast {
delete("${buildDir}/dotnet/DotNet.jar") delete("${buildDir}/dotnet/dotnet.jar")
exec { exec {
workingDir "${project.rootDir}" workingDir "${project.rootDir}"
commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe', commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe',
@ -176,16 +177,8 @@
} }
exec { exec {
commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe", commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe",
"${buildDir}/dotnet/DotNet.dll", "${buildDir}/dotnet/dotnet.dll",
"${buildDir}/dotnet/DotNet0.jar" "${buildDir}/dotnet/dotnet.jar"
}
def manifest = new XmlSlurper().parse(android.sourceSets.main.manifest.srcFile)
exec {
commandLine "${System.env.BLUEBONNET_DIR}/PruneMerge.exe",
"${buildDir}/dotnet/DotNet0.jar",
"${System.env.BLUEBONNET_DIR}/Baselib.jar",
"${buildDir}/dotnet/DotNet.jar",
":${manifest.@package}${manifest.application.activity.'@android:name'}"
} }
} }
} }
@ -239,6 +232,7 @@
dependencies { dependencies {
implementation files("$buildDir/dotnet/dotnet.jar") implementation files("$buildDir/dotnet/dotnet.jar")
implementation files("${System.env.BLUEBONNET_DIR}/baselib.jar")
implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.4.0'
testImplementation 'junit:junit:4.+' testImplementation 'junit:junit:4.+'
@ -248,8 +242,7 @@
task buildDotNet { task buildDotNet {
doLast { doLast {
delete("${buildDir}/dotnet/DotNet.jar") delete("${buildDir}/dotnet/dotnet.jar")
exec { exec {
workingDir "${project.rootDir}" workingDir "${project.rootDir}"
commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe', commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe',
@ -262,23 +255,49 @@
} }
exec { exec {
commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe", commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe",
"${buildDir}/dotnet/DotNet.dll", "${buildDir}/dotnet/dotnet.dll",
"${buildDir}/dotnet/DotNet0.jar" "${buildDir}/dotnet/dotnet.jar"
}
def manifest = new XmlSlurper().parse(android.sourceSets.main.manifest.srcFile)
exec {
commandLine "${System.env.BLUEBONNET_DIR}/PruneMerge.exe",
"${buildDir}/dotnet/DotNet0.jar",
"${System.env.BLUEBONNET_DIR}/Baselib.jar",
"${buildDir}/dotnet/DotNet.jar",
":${manifest.@package}${manifest.application.activity.'@android:name'}"
} }
} }
} }
preBuild.dependsOn buildDotNet preBuild.dependsOn buildDotNet
#### Gradle Build Script - Overview #### ProGuard Rules
- If you wish to minify your release build using Android R8 (ProGuard), enter the following settings into your `app/proguard-rules.pro` file:
#
# these rules prevent discarding of generic types
#
-keepclassmembers class * implements system.IGenericEntity {
public static final java.lang.String ?generic?variance;
public static final *** ?generic?info?class;
private system.RuntimeType ?generic?type;
public static final *** ?generic?info?method (...);
<init>(...);
}
- Only if you wish to use F#, then enter the settings below as well. They are needed with C# code.
#
# F# printf
#
-keepclassmembers class microsoft.fsharp.core.PrintfImpl$ObjectPrinter {
*** GenericToString? (...);
}
-keepclassmembers class microsoft.fsharp.core.PrintfImpl$Specializations* {
*** * (...);
}
-keep class microsoft.fsharp.core.CompilationMappingAttribute { *; }
-keep class **$Tags { *; }
-keepclassmembers class * implements java.io.Serializable {
*** get_* ();
}
-keepattributes InnerClasses
#### To Summarize of All of the Above
- In place of Java source files, a new dependency was added on a JAR file - `dotnet.jar`. - In place of Java source files, a new dependency was added on a JAR file - `dotnet.jar`.
@ -286,17 +305,14 @@
- Run `MSBuild` on the .NET project, in `Release` configuration, with the preprocessor define `ANDROID` - Run `MSBuild` on the .NET project, in `Release` configuration, with the preprocessor define `ANDROID`
- Run `Bluebonnet` on the resulting `dotnet.dll` to create `dotnet0.jar` - Run `Bluebonnet` on the resulting `dotnet.dll` to create `dotnet.jar`
- Extract the class name for the main activity from the file `AndroidManifest.xml`
- Run `PruneMerge` to merge `Baselib.jar` (from Bluebonnet) and `dotnet0.jar` into the final `dotnet.jar`
- All unreferenced classes are discard from the output.
- The `buildDotNet` task was set to execute before the Gradle `preBuild` task. - The `buildDotNet` task was set to execute before the Gradle `preBuild` task.
- ProGuard rules were added to prevent stripping fields and methods used by Bluebonnet to support .NET generic types.
#### Test It #### Test It
- Compile and run the project in Android Studio. - Compile and run the project in Android Studio.

View File

@ -31,26 +31,51 @@ If the input assembly references other assemblies, they are searched in (1) the
.NET C# code which references Java declarations (using a reference assembly, as described above) may use the following syntax to refer to a Java class: .NET C# code which references Java declarations (using a reference assembly, as described above) may use the following syntax to refer to a Java class:
(java.lang.Class) typeof(sometype) C#: (java.lang.Class) typeof(sometype)
F#: java.lang.Class.op_Explicit(typeof<sometype>) (F#)
where `sometype` can be any imported Java class, or non-generic .NET type. where `sometype` can be any imported Java class, or non-generic .NET type.
#### Access to a Java class object #### Delegates to Java functional interface
Java functional interfaces are supported via an artificial delegate, for example: Java functional interfaces are supported via an artificial delegate, for example:
java.lang.Thread.setDefaultUncaughtExceptionHandler( C#: java.lang.Thread.setDefaultUncaughtExceptionHandler(
( (java.lang.Thread.UncaughtExceptionHandler.Delegate) ( ( (java.lang.Thread.UncaughtExceptionHandler.Delegate) (
(java.lang.Thread p1, java.lang.Throwable p2) => (java.lang.Thread p1, java.lang.Throwable p2) =>
{ .... }) ).AsInterface() ); { ...code... }) ).AsInterface() );
In this example, `java.lang.Thread.UncaughtExceptionHandler` is the functional interface, which gets an artificial delegate named `Delegate` as a nested type. The C# lambda is cast to this delegate, and then the `AsInterface` method is invoked, to convert the delegate to a Java interface. F#: java.lang.Thread.setDefaultUncaughtExceptionHandler(
(java.lang.Thread.UncaughtExceptionHandler.Delegate (
fun (p1: java.lang.Thread) (p2: java.lang.Throwable) ->
...code... )).AsInterface())
In this example, `java.lang.Thread.UncaughtExceptionHandler` is the functional interface, which gets an artificial delegate named `Delegate` as a nested type. The lambda is cast to this delegate, and then the `AsInterface` method is invoked, to convert the delegate to a Java interface.
#### Java interfaces in F# code
F# generates [explicit method overrides](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation) for implemented interfaces, which is not compatible with interfaces imported from Java. To work around this, a Java interface must be implemented twice in an F# type:
type MyType () =
// Declare the interface so that it is listed as implemented by the type.
// The actual method implementations are discarded.
interface java.lang.Thread.UncaughtExceptionHandler with
[<java.attr.Discard>] member this.uncaughtException (_, _) = ()
// Provide implicit implementations for the necessary methods.
// Parameters types must match the interface methods.
[<java.attr.RetainName>]
member this.uncaughtException (p1: java.lang.Thread, p2: java.lang.Throwable) = ()
# Attributes # Attributes
Bluebonnet recognizes the following attributes: Bluebonnet recognizes the following attributes:
- `[java.attr.DiscardAttribute]` on a top-level type (class/struct/interface/delegate) to exclude the type from output. For example, [Baselib/`Object.cs`](https://github.com/spaceflint7/bluebonnet/blob/master/Baselib/src/System/Object.cs) declares a `java.lang.Object` type with a `getClass` method, but there is no need to actually emit a Java class for `java.lang.Object`. For an example of this outside of Baselib, see [BNA/`Import.cs`](https://github.com/spaceflint7/bna/blob/master/BNA/src/Import.cs). - `[java.attr.DiscardAttribute]` on a top-level type (class/struct/interface/delegate) or on a class method to exclude the type or method from output. For example, [Baselib/`Object.cs`](https://github.com/spaceflint7/bluebonnet/blob/master/Baselib/src/System/Object.cs) declares a `java.lang.Object` type with a `getClass` method, but there is no need to actually emit a Java class for `java.lang.Object`. For an example of this outside of Baselib, see [BNA/`Import.cs`](https://github.com/spaceflint7/bna/blob/master/BNA/src/Import.cs).
- `[java.attr.RetainTypeAttribute]` on a field data member indicates not to box the field. This is useful for fields that participate in a code hot path, as it eliminates double-references when accessing the field. It should not be used with fields which may be referenced directly from code outside their containing assembly. - `[java.attr.RetainTypeAttribute]` on a field data member indicates not to box the field. This is useful for fields that participate in a code hot path, as it eliminates double-references when accessing the field. It should not be used with fields which may be referenced directly from code outside their containing assembly.