Version 0.2
This commit is contained in:
parent
3d84879ed8
commit
34c96b090c
@ -46,6 +46,7 @@ System.IWellKnownStringEqualityComparer
|
||||
System.DivideByZeroException
|
||||
|
||||
System.FormatException
|
||||
System.FormattableString
|
||||
System.Func`*
|
||||
|
||||
System.ICloneable
|
||||
@ -89,6 +90,7 @@ System.Collections.ObjectModel.ReadOnlyCollection`1
|
||||
System.Collections.HashHelpers
|
||||
|
||||
System.Collections.Generic.Comparer`1
|
||||
System.Collections.Generic.IComparer`1
|
||||
System.Collections.Generic.ComparisonComparer`1
|
||||
System.Collections.Generic.ObjectComparer`1
|
||||
System.Collections.Generic.GenericComparer`1
|
||||
@ -189,13 +191,18 @@ System.Reflection.CallingConventions
|
||||
System.Reflection.IntrospectionExtensions
|
||||
System.Reflection.IReflect
|
||||
System.Reflection.MemberInfo
|
||||
System.Reflection.FieldAttributes
|
||||
System.Reflection.FieldInfo
|
||||
System.Reflection.EventAttributes
|
||||
System.Reflection.EventInfo
|
||||
System.Reflection.ParameterAttributes
|
||||
System.Reflection.ParameterInfo
|
||||
System.Reflection.PropertyAttributes
|
||||
System.Reflection.PropertyInfo
|
||||
System.Reflection.MethodAttributes
|
||||
System.Reflection.MethodInfo
|
||||
System.Reflection.ConstructorInfo
|
||||
System.Reflection.TypeAttributes
|
||||
System.Reflection.TypeInfo
|
||||
System.Reflection.MemberFilter
|
||||
System.Reflection.Missing
|
||||
@ -204,7 +211,6 @@ System.Reflection.InterfaceMapping
|
||||
System.Reflection.InvalidFilterCriteriaException
|
||||
System.Reflection.IReflectableType
|
||||
System.Reflection.ParameterModifier
|
||||
System.Reflection.TypeAttributes
|
||||
System.Reflection.TypeFilter
|
||||
System.Reflection.TargetParameterCountException
|
||||
System.Reflection.TargetInvocationException
|
||||
@ -229,6 +235,7 @@ System.Runtime.CompilerServices.AsyncTaskCache
|
||||
System.Runtime.CompilerServices.AsyncTaskMethodBuilder
|
||||
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`*
|
||||
System.Runtime.CompilerServices.AsyncVoidMethodBuilder
|
||||
System.Runtime.CompilerServices.FormattableStringFactory
|
||||
System.Runtime.CompilerServices.IAsyncStateMachine
|
||||
System.Runtime.CompilerServices.TaskAwaiter
|
||||
System.Runtime.CompilerServices.TaskAwaiter`*
|
||||
|
@ -711,33 +711,34 @@ namespace system
|
||||
//
|
||||
|
||||
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)
|
||||
=> SortGeneric(array, array, 0, -1, false, comparer);
|
||||
public static void Sort<T>(T[] array, IComparer<T> comparer)
|
||||
=> SortGeneric<T>(array, array, 0, -1, false, comparer);
|
||||
|
||||
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)
|
||||
=> SortGeneric(array, array, index, length, true, comparer);
|
||||
public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer)
|
||||
=> SortGeneric<T>(array, array, index, length, true, comparer);
|
||||
|
||||
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)
|
||||
=> SortGeneric(keys, items, 0, -1, false, comparer);
|
||||
public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, IComparer<TKey> comparer)
|
||||
=> SortGeneric<TKey>(keys, items, 0, -1, false, comparer);
|
||||
|
||||
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,
|
||||
system.collections.generic.IComparer<TKey> comparer)
|
||||
=> SortGeneric(keys, items, index, length, true, comparer);
|
||||
IComparer<TKey> comparer)
|
||||
=> SortGeneric<TKey>(keys, items, index, length, true, comparer);
|
||||
|
||||
private static void SortGeneric(object keys_, object items_,
|
||||
int index, int length, bool haveLength,
|
||||
java.util.Comparator comparer)
|
||||
private static void SortGeneric<T>(object keys_, object items_,
|
||||
int index, int length,
|
||||
bool haveLength,
|
||||
IComparer<T> comparer)
|
||||
{
|
||||
ThrowIfNull(keys_);
|
||||
var keys = GetProxy(keys_);
|
||||
@ -749,7 +750,8 @@ namespace system
|
||||
ThrowIfNull(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.
|
||||
// note that we use reflection to call the constructor, which takes
|
||||
// one additional hidden parameter for the generic type.
|
||||
var elementType =
|
||||
system.RuntimeType.GetType(objClass.getComponentType());
|
||||
var newProxyObject =
|
||||
ProxyConstructor.newInstance(new object[] { obj, elementType });
|
||||
ProxyConstructor.newInstance(new object[] {
|
||||
obj, GetArrayElementType(obj, objClass) });
|
||||
proxy = ArrayProxyCache.GetOrAdd(obj, newProxyObject);
|
||||
}
|
||||
if (ok == 2)
|
||||
@ -1526,6 +1527,38 @@ namespace system
|
||||
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)
|
||||
{
|
||||
((Array) GetProxy(obj, cachedArrayType, false)).jagged = true;
|
||||
|
@ -3,30 +3,52 @@ namespace system.collections
|
||||
{
|
||||
|
||||
[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);
|
||||
|
||||
[java.attr.RetainName]
|
||||
public int compareTo(object obj)
|
||||
=> ((System.Collections.IComparer) this).Compare(this, obj);
|
||||
public int compare(object obj1, object obj2)
|
||||
=> 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]
|
||||
public int compare(object obj1, object obj2)
|
||||
=> ((System.Collections.IComparer) this).Compare(obj1, obj2);
|
||||
=> comparer.Compare((T) obj1, (T) obj2);
|
||||
|
||||
[java.attr.RetainName]
|
||||
public bool equals(object obj)
|
||||
=> ((System.Collections.IComparer) this).Compare(this, obj) == 0;
|
||||
public bool equals(object otherComparer)
|
||||
=> 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
|
||||
{
|
||||
|
||||
[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,
|
||||
// see also GenericUtil::CreateGenericVarianceField()
|
||||
@ -34,17 +56,12 @@ namespace system.collections.generic
|
||||
public abstract int Compare(T x, T y);
|
||||
|
||||
[java.attr.RetainName]
|
||||
public int compareTo(object obj)
|
||||
=> ((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);
|
||||
public int compare(object obj1, object obj2) => Compare(obj1, obj2);
|
||||
|
||||
[java.attr.RetainName]
|
||||
public bool equals(object obj)
|
||||
=> ((System.Collections.Generic.IComparer<T>) this).Compare((T) (object) this, (T) obj) == 0;
|
||||
=> throw new System.PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -263,22 +263,26 @@ namespace system
|
||||
|
||||
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();
|
||||
int n = fields.Length;
|
||||
|
||||
var s = asFlags
|
||||
? ToStringMulti(v, fields, n)
|
||||
: ToStringSingle(v, fields, n);
|
||||
? ToStringMulti(v, fields, n, enumLiteralFlags)
|
||||
: ToStringSingle(v, fields, n, enumLiteralFlags);
|
||||
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++)
|
||||
{
|
||||
var f = fields[i];
|
||||
if (f.getModifiers() == ( java.lang.reflect.Modifier.PUBLIC
|
||||
| java.lang.reflect.Modifier.STATIC))
|
||||
if (f.getModifiers() == enumLiteralFlags)
|
||||
{
|
||||
f.setAccessible(true);
|
||||
if (f.getLong(null) == v)
|
||||
@ -288,7 +292,8 @@ namespace system
|
||||
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 sb = new java.lang.StringBuilder();
|
||||
@ -297,8 +302,7 @@ namespace system
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var f = fields[i];
|
||||
if (f.getModifiers() == ( java.lang.reflect.Modifier.PUBLIC
|
||||
| java.lang.reflect.Modifier.STATIC))
|
||||
if (f.getModifiers() == enumLiteralFlags)
|
||||
{
|
||||
f.setAccessible(true);
|
||||
var fv = f.getLong(null);
|
||||
|
@ -89,7 +89,7 @@ namespace system
|
||||
|
||||
if (! ( proxyType is RuntimeType proxyRuntimeType
|
||||
&& proxyRuntimeType.IsCastableToGenericInterface(castToType,
|
||||
(parentObject is system.Array.ProxySyncRoot parentArray) )))
|
||||
(parentObject is system.Array.ProxySyncRoot) )))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@ -183,8 +183,8 @@ namespace system
|
||||
|
||||
public static void ThrowInvalidCastException(object objToCast, System.Type castToType)
|
||||
{
|
||||
string msg = "Unable to cast object of type '" + objToCast.GetType().Name
|
||||
+ "' to type '" + castToType.Name + "'.";
|
||||
string msg = "Unable to cast object of type '" + objToCast.GetType()
|
||||
+ "' to type '" + castToType + "'.";
|
||||
throw new InvalidCastException(msg);
|
||||
}
|
||||
|
||||
@ -200,8 +200,20 @@ namespace system
|
||||
// this is a marker interface used by the system.RuntimeType constructor
|
||||
// to identify generic types. this is implemented by interface types;
|
||||
// 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
|
||||
{
|
||||
// Returns the type of a generic object (i.e. its '-generic-type' field).
|
||||
|
@ -1285,7 +1285,7 @@ namespace system.globalization {
|
||||
var iterator = JavaCollator.getCollationElementIterator("" + ch0);
|
||||
if (iterator.next() != 0)
|
||||
{
|
||||
throw new System.Globalization.CultureNotFoundException(
|
||||
throw new PlatformNotSupportedException(
|
||||
"unexpected value for UNICODE NULL");
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,9 @@ namespace system.globalization {
|
||||
public static CultureInfo CurrentCulture
|
||||
=> system.threading.Thread.CurrentThread.CurrentCulture;
|
||||
|
||||
public static CultureInfo CurrentUICulture
|
||||
=> system.threading.Thread.CurrentThread.CurrentCulture;
|
||||
|
||||
public static CultureInfo InvariantCulture => s_InvariantCultureInfo;
|
||||
|
||||
public virtual object Clone() => throw new System.NotImplementedException();
|
||||
|
@ -9,7 +9,20 @@ namespace system
|
||||
[java.attr.RetainType] private java.util.UUID _uuid;
|
||||
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 string ToString(string format) => ToString(format, null);
|
||||
|
@ -9,11 +9,7 @@ namespace system
|
||||
public abstract int CompareTo(object obj);
|
||||
|
||||
[java.attr.RetainName]
|
||||
public int compareTo(object obj)
|
||||
{
|
||||
//return ((System.IComparable) this).CompareTo(obj);
|
||||
return this.CompareTo(obj);
|
||||
}
|
||||
public int compareTo(object obj) => this.CompareTo(obj);
|
||||
|
||||
}
|
||||
|
||||
|
@ -49,10 +49,28 @@ namespace system
|
||||
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 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 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 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)
|
||||
{
|
||||
// java round clamps the values to Long.MIN_VALUE .. Long.MAX_VALUE
|
||||
@ -74,6 +92,187 @@ namespace system
|
||||
|
||||
public static void OverflowException()
|
||||
=> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
124
Baselib/src/System/Reflection/FSharpCompat.cs
Normal file
124
Baselib/src/System/Reflection/FSharpCompat.cs
Normal 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 { }
|
||||
}
|
@ -51,7 +51,7 @@ namespace system.reflection
|
||||
|
||||
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;
|
||||
|
||||
|
@ -99,6 +99,12 @@ namespace system.reflection
|
||||
throw new NotImplementedException("Assembly.GetCustomAttributes");
|
||||
}
|
||||
|
||||
//
|
||||
// Properties
|
||||
//
|
||||
|
||||
public override bool ReflectionOnly => false;
|
||||
|
||||
//
|
||||
// ISerializable
|
||||
//
|
||||
|
@ -127,7 +127,7 @@ namespace system.reflection
|
||||
object[] parameters, CultureInfo culture)
|
||||
{
|
||||
if (parameters == null)
|
||||
parameters = new object[0];
|
||||
parameters = system.RuntimeType.EmptyObjectArray;
|
||||
int numParameters = parameters.Length;
|
||||
|
||||
if (HasUniqueArg)
|
||||
|
@ -12,6 +12,7 @@ namespace system.reflection
|
||||
{
|
||||
[java.attr.RetainType] public java.lang.reflect.Field JavaField;
|
||||
[java.attr.RetainType] public system.RuntimeType reflectedType;
|
||||
[java.attr.RetainType] private FieldAttributes cachedAttrs = 0;
|
||||
|
||||
//
|
||||
// constructor
|
||||
@ -30,69 +31,83 @@ namespace system.reflection
|
||||
|
||||
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) =>
|
||||
{
|
||||
var javaField = (java.lang.reflect.Field) javaAccessibleObject;
|
||||
javaField.setAccessible(true);
|
||||
list.Add(new RuntimeFieldInfo(javaField, initialType));
|
||||
list.add(new RuntimeFieldInfo(javaField, initialType));
|
||||
return true;
|
||||
});
|
||||
|
||||
return list.ToArray();
|
||||
return (RuntimeFieldInfo[]) list.toArray(new RuntimeFieldInfo[0]);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
public override System.Type FieldType
|
||||
=> system.RuntimeType.GetType(JavaField.getType());
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
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,
|
||||
Binder binder, CultureInfo culture)
|
||||
=> throw new PlatformNotSupportedException();
|
||||
|
||||
public override System.Reflection.FieldAttributes Attributes
|
||||
=> throw new PlatformNotSupportedException();
|
||||
public override FieldAttributes Attributes
|
||||
{
|
||||
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
|
||||
=> throw new PlatformNotSupportedException();
|
||||
if ((modifiers & java.lang.reflect.Modifier.STATIC) != 0)
|
||||
{
|
||||
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
|
||||
=> throw new PlatformNotSupportedException();
|
||||
cachedAttrs = attrs;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
|
||||
public override Type ReflectedType => reflectedType;
|
||||
|
||||
public override Type DeclaringType
|
||||
=> system.RuntimeType.GetType(JavaField.getDeclaringClass());
|
||||
|
||||
public override string Name => JavaField.getName();
|
||||
|
||||
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);
|
||||
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 GetValue(null);
|
||||
}
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace system.reflection
|
||||
string originalName = javaMethod.getName();
|
||||
var compareName = RuntimeMethodInfo.AdjustedMethodName(originalName);
|
||||
|
||||
if (name == compareName)
|
||||
if (name == compareName && CompareParameters(javaMethod, types))
|
||||
{
|
||||
javaMethod.setAccessible(true);
|
||||
var jmodifiers = javaMethod.getModifiers();
|
||||
@ -63,6 +63,26 @@ namespace system.reflection
|
||||
});
|
||||
|
||||
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");
|
||||
if (callConvention != CallingConventions.Any)
|
||||
throw new PlatformNotSupportedException("calling convention must be Any");
|
||||
if (types != null && types.Length != 0)
|
||||
throw new PlatformNotSupportedException("non-null types");
|
||||
if (modifiers != null)
|
||||
throw new PlatformNotSupportedException("non-null modifiers");
|
||||
}
|
||||
@ -337,7 +355,7 @@ namespace system.reflection
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// GetParameters
|
||||
//
|
||||
|
||||
public override ParameterInfo[] GetParameters()
|
||||
|
@ -13,7 +13,7 @@ namespace system.reflection
|
||||
{
|
||||
[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 StructLayoutAttribute StructLayoutAttribute => _StructLayoutAttribute;
|
||||
|
275
Baselib/src/System/Reflection/RuntimePropertyInfo.cs
Normal file
275
Baselib/src/System/Reflection/RuntimePropertyInfo.cs
Normal 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();
|
||||
|
||||
}
|
||||
}
|
564
Baselib/src/System/Reflection/RuntimeType2.cs
Normal file
564
Baselib/src/System/Reflection/RuntimeType2.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -5,10 +5,7 @@ namespace system.resources
|
||||
public class ResourceManager
|
||||
{
|
||||
|
||||
public ResourceManager(string baseName, System.Reflection.Assembly assembly)
|
||||
{
|
||||
Console.WriteLine($"Creating ResourceManager with base name '{baseName}' in assembly '{assembly}'");
|
||||
}
|
||||
public ResourceManager(string baseName, System.Reflection.Assembly assembly) { }
|
||||
|
||||
public string GetString(string name, System.Globalization.CultureInfo culture) => name;
|
||||
|
||||
|
@ -7,8 +7,15 @@ using System.Runtime.Serialization;
|
||||
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]
|
||||
public class RuntimeType : system.reflection.TypeInfo, ISerializable, ICloneable
|
||||
public partial class RuntimeType : system.reflection.TypeInfo,
|
||||
ISerializable, ICloneable
|
||||
{
|
||||
|
||||
private sealed class GenericData
|
||||
@ -89,6 +96,26 @@ namespace system
|
||||
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
|
||||
@ -307,16 +334,17 @@ namespace system
|
||||
|
||||
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;
|
||||
for (;;)
|
||||
{
|
||||
Console.WriteLine("Exception " + ((java.lang.Object) (object) exc).getClass()
|
||||
+ "\nMessage: " + ((java.lang.Throwable) exc).getMessage()
|
||||
+ "\n" + exc.StackTrace);
|
||||
java.lang.System.@out.println(
|
||||
"Exception " + ((java.lang.Object) (object) exc).getClass()
|
||||
+ "\nMessage: " + ((java.lang.Throwable) exc).getMessage()
|
||||
+ "\n" + exc.StackTrace);
|
||||
if ((exc = exc.InnerException) == null)
|
||||
break;
|
||||
Console.Write("Caused by Inner ");
|
||||
java.lang.System.@out.print("Caused by Inner ");
|
||||
}
|
||||
}
|
||||
return exception;
|
||||
@ -419,203 +447,8 @@ namespace system
|
||||
public static bool op_Inequality(RuntimeType obj1, RuntimeType 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 Type UnderlyingSystemType => this;
|
||||
|
||||
// Attributes property
|
||||
@ -665,44 +498,6 @@ namespace system
|
||||
public override bool IsSerializable
|
||||
=> ((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 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()
|
||||
{
|
||||
if (Generic != null)
|
||||
@ -761,7 +546,7 @@ namespace system
|
||||
return (Type[]) java.util.Arrays.copyOf(
|
||||
Generic.ArgumentTypes, Generic.ArgumentTypes.Length);
|
||||
}
|
||||
return new Type[0];
|
||||
return EmptyTypeArray;
|
||||
}
|
||||
|
||||
public override Type GetGenericTypeDefinition()
|
||||
@ -798,8 +583,6 @@ namespace system
|
||||
// Primitives types
|
||||
//
|
||||
|
||||
|
||||
|
||||
protected override TypeCode GetTypeCodeImpl()
|
||||
=> (TypeCode) (((int) (CachedAttrs & AttrTypeCode) >> 24) + 1);
|
||||
|
||||
@ -905,7 +688,7 @@ namespace system
|
||||
|
||||
public object CallConstructor(bool publicOnly)
|
||||
{
|
||||
object[] args = (Generic == null) ? new object[0] : Generic.ArgumentTypes;
|
||||
object[] args = (Generic == null) ? EmptyObjectArray : Generic.ArgumentTypes;
|
||||
try
|
||||
{
|
||||
#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
|
||||
//
|
||||
|
||||
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type[] argTypes)
|
||||
{
|
||||
var key = new TypeKey(cls, argTypes);
|
||||
@ -1376,6 +1086,9 @@ namespace system
|
||||
|
||||
if (TypeCache == null)
|
||||
{
|
||||
EmptyObjectArray = new object[0];
|
||||
EmptyTypeArray = new Type[0];
|
||||
|
||||
TypeCache = new java.util.concurrent.ConcurrentHashMap();
|
||||
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.locks.ReentrantLock TypeLock;
|
||||
[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:
|
||||
// base implementations throw NotSupportedException
|
||||
|
@ -143,6 +143,13 @@ namespace system.text
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringBuilder Append(object value)
|
||||
{
|
||||
if (value != null)
|
||||
sb.append(value.ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public int Capacity
|
||||
{
|
||||
get => sb.capacity();
|
||||
|
@ -171,6 +171,18 @@ namespace SpaceFlint.CilToJava
|
||||
public List<CilType> Parameters;
|
||||
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)
|
||||
@ -186,11 +198,14 @@ namespace SpaceFlint.CilToJava
|
||||
if (idx != -1)
|
||||
name = name.Substring(idx + 1);
|
||||
|
||||
ResolvedGenericTypes = "";
|
||||
|
||||
var returnType = (CilType) fromMethod.ReturnType;
|
||||
if (returnType.IsGenericParameter)
|
||||
{
|
||||
var genericMark = CilMain.GenericStack.Mark();
|
||||
var (type, index) = CilMain.GenericStack.Resolve(returnType.JavaName);
|
||||
ResolvedGenericTypes = $"{type},{index};";
|
||||
if (index == 0)
|
||||
returnType = type;
|
||||
|
||||
@ -208,6 +223,7 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
var genericMark = CilMain.GenericStack.Mark();
|
||||
var (type, index) = CilMain.GenericStack.Resolve(parameter.JavaName);
|
||||
ResolvedGenericTypes += $"{type},{index};";
|
||||
if (index == 0)
|
||||
{
|
||||
if (parameter.ArrayRank != 0)
|
||||
@ -342,6 +358,9 @@ namespace SpaceFlint.CilToJava
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fromMethod.HasCustomAttribute("Discard"))
|
||||
continue; // skip if decorated with [java.attr.Discard]
|
||||
|
||||
var genericMark = CilMain.GenericStack.Mark();
|
||||
var inputMethod = CilMain.GenericStack.EnterMethod(fromMethod);
|
||||
|
||||
|
@ -67,7 +67,8 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
|
||||
internal static JavaClass CreateInnerClass(JavaClass outerClass, string innerName,
|
||||
JavaAccessFlags innerFlags = 0)
|
||||
JavaAccessFlags innerFlags = 0,
|
||||
bool markGenericEntity = false)
|
||||
{
|
||||
if (innerFlags == 0)
|
||||
{
|
||||
@ -85,6 +86,9 @@ namespace SpaceFlint.CilToJava
|
||||
innerClass.Fields = new List<JavaField>();
|
||||
innerClass.Methods = new List<JavaMethod>();
|
||||
|
||||
if (markGenericEntity)
|
||||
innerClass.AddInterface("system.IGenericEntity");
|
||||
|
||||
outerClass.AddInnerClass(innerClass);
|
||||
return innerClass;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ namespace SpaceFlint.CilToJava
|
||||
Flags |= CONSTRUCTOR;
|
||||
Flags |= THIS_ARG | ARRAY_CALL;
|
||||
|
||||
ImportParameters(fromMethod);
|
||||
ImportParameters(fromMethod, null);
|
||||
ImportGenericParameters(fromMethod);
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ namespace SpaceFlint.CilToJava
|
||||
DeclType = CilType.From(fromMethod.DeclaringType);
|
||||
|
||||
SetMethodType(defMethod);
|
||||
bool appendSuffix = ImportParameters(fromMethod);
|
||||
bool appendSuffix = ImportParameters(fromMethod, defMethod);
|
||||
TranslateNameClrToJvm(fromMethod, appendSuffix);
|
||||
|
||||
if (IsConstructor)
|
||||
@ -166,7 +166,7 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
|
||||
|
||||
bool ImportParameters(MethodReference fromMethod)
|
||||
bool ImportParameters(MethodReference fromMethod, MethodDefinition defMethod)
|
||||
{
|
||||
bool appendSuffix = false;
|
||||
|
||||
@ -194,7 +194,10 @@ namespace SpaceFlint.CilToJava
|
||||
nm += "array-" + paramType.ArrayRank + "-";
|
||||
if (paramType.IsByReference)
|
||||
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);
|
||||
Flags |= GEN_ARGS;
|
||||
}
|
||||
@ -275,6 +278,70 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
@ -876,5 +959,8 @@ namespace SpaceFlint.CilToJava
|
||||
internal static readonly JavaMethodRef ValueClone =
|
||||
new JavaMethod("system-ValueMethod-Clone", CilType.SystemValueType);
|
||||
|
||||
internal static readonly JavaType SystemIDisposable =
|
||||
new JavaType(0, 0, "system.IDisposable");
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ namespace SpaceFlint.CilToJava
|
||||
internal JavaStackMap stackMap;
|
||||
|
||||
internal int lineNumber;
|
||||
internal int numCastableInterfaces;
|
||||
|
||||
|
||||
|
||||
@ -51,8 +52,9 @@ namespace SpaceFlint.CilToJava
|
||||
o.defMethodBody = defMethod.Body;
|
||||
o.newMethod = newMethod;
|
||||
o.method = myMethod;
|
||||
o.numCastableInterfaces = numCastableInterfaces;
|
||||
|
||||
o.Process(numCastableInterfaces);
|
||||
o.Process();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -77,7 +79,7 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
|
||||
|
||||
void Process(int numCastableInterfaces)
|
||||
void Process()
|
||||
{
|
||||
code = newMethod.Code = new JavaCode(newMethod);
|
||||
var oldLabel = code.SetLabel(0xFFFF);
|
||||
@ -86,7 +88,7 @@ namespace SpaceFlint.CilToJava
|
||||
arrays = new CodeArrays(code, locals);
|
||||
exceptions = new CodeExceptions(defMethodBody, code, locals);
|
||||
|
||||
InsertMethodInitCode(numCastableInterfaces);
|
||||
InsertMethodInitCode();
|
||||
|
||||
code.SetLabel(oldLabel);
|
||||
|
||||
@ -101,7 +103,7 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
|
||||
|
||||
void InsertMethodInitCode(int numCastableInterfaces)
|
||||
void InsertMethodInitCode()
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
@ -135,7 +137,7 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
// init the array of generic interfaces
|
||||
InterfaceBuilder.InitInterfaceArrayField(
|
||||
method.DeclType, numCastableInterfaces, code);
|
||||
method.DeclType, numCastableInterfaces, code, 0);
|
||||
|
||||
// in any constructor, we want to allocate boxed instance fields
|
||||
ValueUtil.InitializeInstanceFields(newMethod.Class, method.DeclType,
|
||||
|
@ -180,6 +180,7 @@ namespace SpaceFlint.CilToJava
|
||||
var currentClass = method.DeclType;
|
||||
var callClass = callMethod.DeclType;
|
||||
|
||||
bool isCallToClone = false;
|
||||
byte op;
|
||||
if (callMethod.IsStatic)
|
||||
{
|
||||
@ -245,10 +246,7 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
|
||||
if (callMethod.Name == "clone" && callMethod.Parameters.Count == 0)
|
||||
{
|
||||
// if calling clone on the super object, implement Cloneable
|
||||
code.Method.Class.AddInterface("java.lang.Cloneable");
|
||||
}
|
||||
isCallToClone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -272,6 +270,22 @@ namespace SpaceFlint.CilToJava
|
||||
ClearMethodArguments(callMethod, (op == 0xB7));
|
||||
|
||||
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;
|
||||
@ -306,7 +320,12 @@ namespace SpaceFlint.CilToJava
|
||||
callClass = CilType.From(CilType.SystemValueType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ConvertInterfaceCall(callClass, callMethod))
|
||||
return true;
|
||||
|
||||
op = 0xB9; // invokeinterface
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
var typeRef = data as TypeReference;
|
||||
|
@ -763,6 +763,8 @@ namespace SpaceFlint.CilToJava
|
||||
indexLocalVars1 += indexLocalVars0;*/
|
||||
int resetLength = resetLocals.Length;
|
||||
|
||||
bool canBreakEarly = true;
|
||||
|
||||
var inst = targetInst;
|
||||
while (inst != branchInst)
|
||||
{
|
||||
@ -794,6 +796,29 @@ namespace SpaceFlint.CilToJava
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,10 @@ namespace SpaceFlint.CilToJava
|
||||
byte op;
|
||||
if (! isStatic)
|
||||
{
|
||||
if (method.IsConstructor &&
|
||||
LoadFieldInConstructor(fldName, fldType, fldClass))
|
||||
return true;
|
||||
|
||||
PopObjectAndLoadFromSpan(fldClass);
|
||||
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 isStatic, bool isVolatile)
|
||||
{
|
||||
|
@ -461,9 +461,10 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
else
|
||||
{
|
||||
if (localType.IsGenericParameter && (! localType.IsByReference))
|
||||
if (localType.IsGenericParameter)
|
||||
{
|
||||
GenericUtil.ValueLoad(code);
|
||||
if (! localType.IsByReference)
|
||||
GenericUtil.ValueLoad(code);
|
||||
localType = CilType.From(JavaType.ObjectType);
|
||||
}
|
||||
|
||||
@ -612,7 +613,7 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
public List<(CilType, int)> GetUninitializedVariables()
|
||||
{
|
||||
List<(CilType, int)> list = null;
|
||||
@ -629,7 +630,7 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
public (CilType, int) GetLocalFromLoadInst(Code op, object data)
|
||||
|
@ -346,13 +346,17 @@ namespace SpaceFlint.CilToJava
|
||||
if (CodeSpan.LoadStore(false, intoType, null, dataType, code))
|
||||
return;
|
||||
|
||||
if ( (! dataType.IsReference)
|
||||
&& intoType is BoxedType intoBoxedType
|
||||
&& dataType.PrimitiveType == intoBoxedType.UnboxedType.PrimitiveType)
|
||||
if ( intoType is BoxedType intoBoxedType
|
||||
&& dataType.PrimitiveType ==
|
||||
intoBoxedType.UnboxedType.PrimitiveType)
|
||||
{
|
||||
// 'stobj primitive' with a primitive value on the stack
|
||||
intoBoxedType.SetValueOV(code);
|
||||
return;
|
||||
// 'stobj primitive' with a primitive value on the stack,
|
||||
// or 'stobj primitive[]' with a primitive array on the stack
|
||||
if ((! dataType.IsReference) || dataType.IsArray)
|
||||
{
|
||||
intoBoxedType.SetValueOV(code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
code.StackMap.PushStack(intoType);
|
||||
|
@ -178,7 +178,8 @@ namespace SpaceFlint.CilToJava
|
||||
//
|
||||
|
||||
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
|
||||
@ -345,13 +346,14 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
if (! anyVariance)
|
||||
{
|
||||
/* removed; see IComparer.cs in baselib
|
||||
if (fromType.JavaName == "system.collections.generic.IComparer$$1")
|
||||
{
|
||||
// force a variance string for an interface that we create
|
||||
// as an abstract class; see also IComparer.cs in baselib
|
||||
varianceString = "I";
|
||||
}
|
||||
else
|
||||
else*/
|
||||
return;
|
||||
}
|
||||
|
||||
@ -487,6 +489,16 @@ namespace SpaceFlint.CilToJava
|
||||
{
|
||||
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
|
||||
|
||||
code.NewInstruction(0x19 /* aload */, null, (int) 0);
|
||||
|
@ -74,7 +74,6 @@ namespace SpaceFlint.CilToJava
|
||||
// the RuntimeType constructor in baselib uses IGenericEntity
|
||||
// marker interface to identify generic classes. note that
|
||||
// real generic types implement IGenericObject -> IGenericEntity.
|
||||
|
||||
theClass.AddInterface("system.IGenericEntity");
|
||||
}
|
||||
|
||||
@ -124,14 +123,14 @@ namespace SpaceFlint.CilToJava
|
||||
// if the class implements a generic interface for multiple types,
|
||||
// then we need a method suffix to differentiate between the methods.
|
||||
// see also: CilMethod::InsertMethodNamePrefix
|
||||
string methodSuffix = "";
|
||||
/*string methodSuffix = "";
|
||||
foreach (var genericType in ifc.GenericTypes)
|
||||
methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType);
|
||||
methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType);*/
|
||||
|
||||
foreach (var ifcMethod in ifc.Methods)
|
||||
{
|
||||
// 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.
|
||||
|
||||
var newClass = CilMain.CreateInnerClass(parentClass,
|
||||
parentClass.Name + "$$generic" + ifcNumber.ToString());
|
||||
parentClass.Name + "$$generic" + ifcNumber.ToString(),
|
||||
markGenericEntity: true);
|
||||
|
||||
var fld = new JavaField();
|
||||
fld.Name = ParentFieldName;
|
||||
@ -199,7 +199,7 @@ namespace SpaceFlint.CilToJava
|
||||
//
|
||||
|
||||
public static void InitInterfaceArrayField(CilType toType, int numCastableInterfaces,
|
||||
JavaCode code)
|
||||
JavaCode code, int objectIndex)
|
||||
{
|
||||
// if a type has castable interface (as counted by CastableInterfaceCount),
|
||||
// then we need to initialize the helper array field, for use by the
|
||||
@ -208,8 +208,14 @@ namespace SpaceFlint.CilToJava
|
||||
if (numCastableInterfaces == 0)
|
||||
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.NewInstruction(0xBB /* new */, AtomicReferenceArrayType, null);
|
||||
code.StackMap.PushStack(AtomicReferenceArrayType);
|
||||
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,
|
||||
JavaClass ifcClass)
|
||||
{
|
||||
@ -408,9 +414,23 @@ namespace SpaceFlint.CilToJava
|
||||
{
|
||||
// more than one method may match, if a derived type overrides
|
||||
// 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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +100,24 @@ namespace SpaceFlint.CilToJava
|
||||
// Android 'D8' desugars static methods on an interface by
|
||||
// moving into a separate class, so we do it ourselves.
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
@ -347,10 +363,12 @@ namespace SpaceFlint.CilToJava
|
||||
attrs &= ~FieldAttributes.Static;
|
||||
}
|
||||
|
||||
if ((attrs & FieldAttributes.InitOnly) != 0)
|
||||
if (0 != (attrs & ( FieldAttributes.InitOnly
|
||||
| FieldAttributes.Literal)))
|
||||
{
|
||||
flags |= JavaAccessFlags.ACC_FINAL;
|
||||
attrs &= ~FieldAttributes.InitOnly;
|
||||
attrs &= ~( FieldAttributes.InitOnly
|
||||
| FieldAttributes.Literal);
|
||||
}
|
||||
|
||||
if ((attrs & FieldAttributes.NotSerialized) != 0)
|
||||
@ -359,8 +377,7 @@ namespace SpaceFlint.CilToJava
|
||||
attrs &= ~FieldAttributes.NotSerialized;
|
||||
}
|
||||
|
||||
attrs &= ~( FieldAttributes.Literal
|
||||
| FieldAttributes.HasFieldRVA
|
||||
attrs &= ~( FieldAttributes.HasFieldRVA
|
||||
| FieldAttributes.HasDefault
|
||||
| FieldAttributes.SpecialName
|
||||
| FieldAttributes.RTSpecialName);
|
||||
@ -385,10 +402,8 @@ namespace SpaceFlint.CilToJava
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var defMethod = cilType.Methods[i];
|
||||
/*
|
||||
if (defMethod.HasCustomAttribute("Discard"))
|
||||
continue; // if decorated with [java.attr.Discard], don't export to java
|
||||
*/
|
||||
|
||||
var genericMark = CilMain.GenericStack.Mark();
|
||||
var myMethod = CilMain.GenericStack.EnterMethod(defMethod);
|
||||
|
@ -18,7 +18,7 @@ namespace SpaceFlint.CilToJava
|
||||
valueClass.Super = CilType.SystemValueType.ClassName;
|
||||
|
||||
CreateDefaultConstructor(valueClass, fromType, numCastableInterfaces, true);
|
||||
CreateValueMethods(valueClass, fromType);
|
||||
CreateValueMethods(valueClass, fromType, numCastableInterfaces);
|
||||
|
||||
if ((valueClass.Flags & JavaAccessFlags.ACC_ABSTRACT) == 0)
|
||||
valueClass.Flags |= JavaAccessFlags.ACC_FINAL;
|
||||
@ -58,7 +58,8 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
|
||||
// init the array of generic interfaces
|
||||
InterfaceBuilder.InitInterfaceArrayField(fromType, numCastableInterfaces, code);
|
||||
InterfaceBuilder.InitInterfaceArrayField(
|
||||
fromType, numCastableInterfaces, code, 0);
|
||||
|
||||
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);
|
||||
CreateValueCopyToMethod(valueClass, fromType);
|
||||
CreateValueCloneMethod(valueClass, fromType);
|
||||
CreateValueCloneMethod(valueClass, fromType, numCastableInterfaces);
|
||||
|
||||
//
|
||||
// system-ValueMethod-Clear() resets all fields to their default value
|
||||
@ -180,9 +182,11 @@ namespace SpaceFlint.CilToJava
|
||||
// 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;
|
||||
|
||||
code.NewInstruction(0x19 /* aload */, null, (int) 0);
|
||||
@ -215,7 +219,15 @@ namespace SpaceFlint.CilToJava
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -189,6 +189,8 @@ namespace SpaceFlint.JavaBinary
|
||||
|
||||
else if (inst.Data is double vDouble)
|
||||
{
|
||||
if (FillInstruction_ConstLoad_Double(inst, vDouble))
|
||||
return;
|
||||
constantIndex = wtr.ConstDouble(vDouble);
|
||||
op = 0x14;
|
||||
}
|
||||
@ -215,7 +217,11 @@ namespace SpaceFlint.JavaBinary
|
||||
}
|
||||
|
||||
else if (inst.Data is float vFloat)
|
||||
{
|
||||
if (FillInstruction_ConstLoad_Float(inst, vFloat))
|
||||
return;
|
||||
constantIndex = wtr.ConstFloat(vFloat);
|
||||
}
|
||||
|
||||
else if (inst.Data is string 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)
|
||||
{
|
||||
if (op == 0x84)
|
||||
|
@ -589,7 +589,11 @@ namespace SpaceFlint.JavaBinary
|
||||
offset = (ushort) (offset - (lastOffset + 1));
|
||||
item.deltaOffset = offset;
|
||||
|
||||
// discard any trailing 'top' elements for out-of-scape locals
|
||||
int numLocals = frame.locals.Count;
|
||||
while (numLocals > 0 && frame.locals[numLocals - 1].Equals(Top))
|
||||
numLocals--;
|
||||
|
||||
var localsList = new List<JavaAttribute.StackMapTable.Slot>(numLocals);
|
||||
for (int i = 0; i < numLocals; i += frame.locals[i].Category)
|
||||
localsList.Add(JavaTypeToVerificationType(frame.locals[i], wtr));
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 spaceflint7
|
||||
Copyright (c) 2020, 2021 spaceflint7
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
@ -363,7 +379,12 @@ public class DotNetImporter
|
||||
{
|
||||
module.Types.Add(cilType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void LinkCilTypesByClass(TypeDefinition cilType, JavaClass jclass)
|
||||
{
|
||||
var superName = jclass.Super;
|
||||
if (superName == "java.lang.Enum")
|
||||
superName = null;
|
||||
@ -379,6 +400,16 @@ public class DotNetImporter
|
||||
cilType.Interfaces.Add(
|
||||
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
|
||||
{
|
||||
@ -707,7 +738,7 @@ public class DotNetImporter
|
||||
var method = new MethodDefinition(
|
||||
"op_Explicit", attrs, CilTypeReference(JavaType.ClassType));
|
||||
method.Parameters.Add(new ParameterDefinition(
|
||||
"type", 0, new TypeReference("System", "Type",
|
||||
"source", 0, new TypeReference("System", "Type",
|
||||
module, module.TypeSystem.CoreLibrary)));
|
||||
SetCommonMethodBody(method);
|
||||
cilType.Methods.Add(method);
|
||||
|
@ -44,7 +44,7 @@ public class DotNetPrinter
|
||||
{
|
||||
txt.Write("{0}{1}",
|
||||
(comma ? ", " : string.Empty),
|
||||
intrface.InterfaceType.FullName);
|
||||
intrface.InterfaceType?.FullName ?? "(null)");
|
||||
|
||||
comma = true;
|
||||
}
|
||||
|
@ -383,6 +383,20 @@ namespace Tests
|
||||
{
|
||||
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)
|
||||
@ -425,6 +439,21 @@ namespace Tests
|
||||
list.Add(arr1);
|
||||
var arr2 = list.ToArray();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ namespace Tests
|
||||
TestGenericInterface2();
|
||||
TestGenericOverload3();
|
||||
TestGenericMethodWithEnum();
|
||||
TestGenericByRef();
|
||||
}
|
||||
|
||||
//
|
||||
@ -179,7 +180,7 @@ namespace Tests
|
||||
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(T2 v) => 4;
|
||||
@ -190,6 +191,8 @@ namespace Tests
|
||||
var a = new MyOv1<int,bool>();
|
||||
Console.Write(a.Get(1));
|
||||
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>();
|
||||
Console.Write(b.Get(1));
|
||||
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 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 CccC4<T0,T2,T1> : CccB1<T1,T2>{ public override void DoIt(ref T1 a, ref T2 b) => Console.Write("OK4 "); }
|
||||
|
||||
void TestGenericOverload3()
|
||||
{
|
||||
@ -235,10 +239,12 @@ namespace Tests
|
||||
CccB1<bool,int> c1 = new CccC1<bool>();
|
||||
CccB1<bool,Version> c2 = new CccC2<bool>();
|
||||
CccB1<bool,object> c3 = new CccC3<bool>();
|
||||
CccB1<bool,object>c4 = new CccC4<int,object,bool>();
|
||||
|
||||
c1.DoIt(ref bFalse, ref iZero);
|
||||
c2.DoIt(ref bFalse, ref vZero);
|
||||
c3.DoIt(ref bFalse, ref oZero);
|
||||
c4.DoIt(ref bFalse, ref oZero);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,9 @@ namespace Tests
|
||||
DisplayGenericType(tT, "Type parameter T from Base<T>");
|
||||
//DisplayGenericType(tF, "Field type, G<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)
|
||||
|
@ -13,6 +13,7 @@ namespace Tests
|
||||
{
|
||||
TestSuppressGC();
|
||||
TestUnhandledException();
|
||||
TestUsing();
|
||||
}
|
||||
|
||||
//
|
||||
@ -46,6 +47,26 @@ namespace Tests
|
||||
=> 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
- 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.
|
||||
@ -20,10 +20,10 @@
|
||||
#### Environment Variables
|
||||
|
||||
- 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.
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
- 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:
|
||||
@ -158,12 +158,13 @@
|
||||
- At the top of the `dependencies` section, insert:
|
||||
|
||||
implementation files("$buildDir/dotnet/dotnet.jar")
|
||||
implementation files("${System.env.BLUEBONNET_DIR}/baselib.jar")
|
||||
|
||||
- After the `dependencies` section, append:
|
||||
|
||||
task buildDotNet {
|
||||
doLast {
|
||||
delete("${buildDir}/dotnet/DotNet.jar")
|
||||
delete("${buildDir}/dotnet/dotnet.jar")
|
||||
exec {
|
||||
workingDir "${project.rootDir}"
|
||||
commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe',
|
||||
@ -176,16 +177,8 @@
|
||||
}
|
||||
exec {
|
||||
commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe",
|
||||
"${buildDir}/dotnet/DotNet.dll",
|
||||
"${buildDir}/dotnet/DotNet0.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'}"
|
||||
"${buildDir}/dotnet/dotnet.dll",
|
||||
"${buildDir}/dotnet/dotnet.jar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,6 +232,7 @@
|
||||
dependencies {
|
||||
|
||||
implementation files("$buildDir/dotnet/dotnet.jar")
|
||||
implementation files("${System.env.BLUEBONNET_DIR}/baselib.jar")
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
@ -248,8 +242,7 @@
|
||||
|
||||
task buildDotNet {
|
||||
doLast {
|
||||
delete("${buildDir}/dotnet/DotNet.jar")
|
||||
|
||||
delete("${buildDir}/dotnet/dotnet.jar")
|
||||
exec {
|
||||
workingDir "${project.rootDir}"
|
||||
commandLine System.env.MSBUILD_EXE ?: 'msbuild.exe',
|
||||
@ -262,23 +255,49 @@
|
||||
}
|
||||
exec {
|
||||
commandLine "${System.env.BLUEBONNET_DIR}/Bluebonnet.exe",
|
||||
"${buildDir}/dotnet/DotNet.dll",
|
||||
"${buildDir}/dotnet/DotNet0.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'}"
|
||||
"${buildDir}/dotnet/dotnet.dll",
|
||||
"${buildDir}/dotnet/dotnet.jar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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`.
|
||||
|
||||
@ -286,17 +305,14 @@
|
||||
|
||||
- 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.
|
||||
|
||||
- ProGuard rules were added to prevent stripping fields and methods used by Bluebonnet to support .NET generic types.
|
||||
|
||||
#### Test It
|
||||
|
||||
- Compile and run the project in Android Studio.
|
||||
|
39
USAGE.md
39
USAGE.md
@ -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:
|
||||
|
||||
(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.
|
||||
|
||||
#### Access to a Java class object
|
||||
#### Delegates to Java functional interface
|
||||
|
||||
Java functional interfaces are supported via an artificial delegate, for example:
|
||||
|
||||
java.lang.Thread.setDefaultUncaughtExceptionHandler(
|
||||
( (java.lang.Thread.UncaughtExceptionHandler.Delegate) (
|
||||
C#: java.lang.Thread.setDefaultUncaughtExceptionHandler(
|
||||
( (java.lang.Thread.UncaughtExceptionHandler.Delegate) (
|
||||
(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
|
||||
|
||||
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.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user