Version 0.2

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

View File

@ -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`*

View File

@ -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;

View File

@ -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();
}
}
*/

View File

@ -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);

View File

@ -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).

View File

@ -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");
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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);
}

View File

@ -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));
}
}
}

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -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();
}

View File

@ -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()

View File

@ -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;

View File

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

View File

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

View File

@ -5,10 +5,7 @@ namespace system.resources
public class ResourceManager
{
public 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;

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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)
{

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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));

View File

@ -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

View File

@ -182,6 +182,22 @@ public class DotNetImporter
}
}
// arrange Java inner classes as CIL nested classes,
// and add Java outer classes to the CIL module itself
foreach (var jclass in classes2)
{
Where.Push($"class '{jclass.Name}'");
var cilType = typeMap[jclass.Name] as TypeDefinition;
AddClassToDeclaringType(cilType, jclass);
Where.Pop();
}
// establish links between classes and their
// super classes and interfaces
foreach (var jclass in classes2)
{
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);

View File

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

View File

@ -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();
}
}

View File

@ -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));
}
}
}
}

View File

@ -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)

View File

@ -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();
}
}
}

View File

@ -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.

View File

@ -31,26 +31,51 @@ If the input assembly references other assemblies, they are searched in (1) the
.NET C# code which references Java declarations (using a reference assembly, as described above) may use the following syntax to refer to a Java class:
(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.