From 5aa43c15eeb164e8ca862eceab3bd724bb5d75de Mon Sep 17 00:00:00 2001 From: spaceflint <> Date: Tue, 1 Sep 2020 18:28:45 +0300 Subject: [PATCH] incremental changes --- Baselib/mscorlib.filter | 8 + Baselib/src/System/Collections/IComparer.cs | 7 + Baselib/src/System/Enum.cs | 224 ++++++++++++++---- Baselib/src/System/GenericType.cs | 6 + .../src/System/Globalization/CultureInfo.cs | 3 + Baselib/src/System/Int16.cs | 46 +++- Baselib/src/System/Int32.cs | 41 +++- Baselib/src/System/Int64.cs | 81 ++++++- .../System/Reflection/BindingFlagsIterator.cs | 12 + .../src/System/Reflection/RuntimeFieldInfo.cs | 38 ++- .../System/Reflection/RuntimeMethodInfo.cs | 104 +++----- .../src/System/Reflection/RuntimeModule.cs | 2 + Baselib/src/System/RuntimeType.cs | 50 +++- Baselib/src/System/Span.cs | 53 ++++- Baselib/src/System/ValueType.cs | 10 + CilToJava/src/CilMain.cs | 14 ++ CilToJava/src/CilType.cs | 10 +- CilToJava/src/CodeArray.cs | 3 +- CilToJava/src/CodeCompare.cs | 17 +- CilToJava/src/CodeMisc.cs | 10 +- CilToJava/src/CodeNumber.cs | 18 +- CilToJava/src/CodeSpan.cs | 157 ++++++++++-- CilToJava/src/GenericUtil.cs | 33 ++- Tests/src/TestPointer.cs | 16 +- Tests/src/TestValue.cs | 10 +- 25 files changed, 773 insertions(+), 200 deletions(-) diff --git a/Baselib/mscorlib.filter b/Baselib/mscorlib.filter index 032fb8f..d9acd4c 100644 --- a/Baselib/mscorlib.filter +++ b/Baselib/mscorlib.filter @@ -16,9 +16,12 @@ System.ArithmeticException System.ArrayTypeMismatchException System.ApplicationException System.Comparison`* +System.Convert System.Converter`* System.DateTime +System.DBNull System.DllNotFoundException +System.Empty System.EventArgs System.EventHandler System.EventHandler`* @@ -49,6 +52,7 @@ System.Nullable`* System.SystemException System.OverflowException +System.Collections.Comparer System.Collections.ICollection System.Collections.IDictionary System.Collections.IDictionaryEnumerator @@ -147,6 +151,7 @@ System.Reflection.Assembly System.Reflection.AssemblyName System.Reflection.AssemblyNameFlags System.Reflection.AssemblyProductAttribute +System.Reflection.Binder System.Reflection.BindingFlags System.Reflection.CallingConventions System.Reflection.IReflect @@ -160,9 +165,12 @@ System.Reflection.TypeInfo System.Reflection.MemberFilter System.Reflection.Missing System.Reflection.ICustomAttributeProvider +System.Reflection.InterfaceMapping System.Reflection.InvalidFilterCriteriaException System.Reflection.IReflectableType +System.Reflection.ParameterModifier System.Reflection.TypeAttributes +System.Reflection.TypeFilter System.Reflection.TargetParameterCountException System.Reflection.TargetInvocationException System.IRuntimeMethodInfo diff --git a/Baselib/src/System/Collections/IComparer.cs b/Baselib/src/System/Collections/IComparer.cs index 086e392..e12517d 100644 --- a/Baselib/src/System/Collections/IComparer.cs +++ b/Baselib/src/System/Collections/IComparer.cs @@ -5,6 +5,8 @@ namespace system.collections [java.attr.AsInterface] public abstract class IComparer : java.lang.Comparable, 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); @@ -26,6 +28,11 @@ namespace system.collections.generic [java.attr.AsInterface] public abstract class IComparer : java.lang.Comparable, java.util.Comparator { + // a generic variance field is created for this abstract class, + // see also GenericUtil::CreateGenericVarianceField() + + public abstract int Compare(T x, T y); + [java.attr.RetainName] public int compareTo(object obj) => ((System.Collections.Generic.IComparer) this).Compare((T) (object) this, (T) obj); diff --git a/Baselib/src/System/Enum.cs b/Baselib/src/System/Enum.cs index a9e9e6a..3265545 100644 --- a/Baselib/src/System/Enum.cs +++ b/Baselib/src/System/Enum.cs @@ -1,4 +1,6 @@ +using System.Globalization; + namespace system { @@ -6,9 +8,12 @@ namespace system [System.Serializable] public abstract class Enum : system.ValueType, system.ValueMethod, System.IComparable, - System.IFormattable //System.IConvertible + System.IFormattable, System.IConvertible { + // warning, do not add fields in this class. + // doing so will interfere with enumeration of constants. + // // getters and setters // @@ -80,31 +85,105 @@ namespace system // - // GetUnderlyingType + // ToObject + // + + public static object ToObject(System.Type enumType, long value) + { + ThrowHelper.ThrowIfNull(enumType); + if (! enumType.IsEnum) + throw new System.ArgumentException(); + var enumRuntimeType = enumType as RuntimeType; + if (enumRuntimeType == null) + throw new System.ArgumentException(); + return Box(value, enumRuntimeType.JavaClassForArray()); + } + + public static object ToObject(System.Type enumType, ulong value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, uint value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, int value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, ushort value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, short value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, char value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, byte value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, sbyte value) + => ToObject(enumType, (long) value); + + public static object ToObject(System.Type enumType, bool value) + => ToObject(enumType, value ? 1L : 0L); + + + + // + // HasFlag + // + + public bool HasFlag(Enum flag) + { + ThrowHelper.ThrowIfNull(flag); + if (! this.GetType().IsEquivalentTo(flag.GetType())) + throw new System.ArgumentException(); + var v = flag.GetLong(); + return (GetLong() & v) == v; + } + + + + // + // implemented via System.Type: GetUnderlyingType, GetTypeCode, + // IsDefined, GetName, GetNames, GetValues // public static System.Type GetUnderlyingType(System.Type enumType) { ThrowHelper.ThrowIfNull(enumType); return enumType.GetEnumUnderlyingType(); - #if false - if (enumType.IsEnum && enumType is RuntimeType enumRuntimeType) - { - var fields = enumRuntimeType.JavaClassForArray().getDeclaredFields(); - if (fields.Length > 1) - { - var f = fields[0]; - if ((f.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) - { - var fldType = system.RuntimeType.GetType(f.getType()); - var typeCode = (int) System.Type.GetTypeCode(fldType); - if (typeCode >= 4 && typeCode <= 12) - return fldType; - } - } - } - throw new System.ArgumentException(); - #endif + } + + public System.TypeCode GetTypeCode() + { + var typeCode = System.Type.GetTypeCode(GetType().GetEnumUnderlyingType()); + if ((int) typeCode >= 3 && (int) typeCode <= 12) // boolean .. uint64 + return typeCode; + throw new System.InvalidOperationException(); + } + + public static bool IsDefined(System.Type enumType, object value) + { + ThrowHelper.ThrowIfNull(enumType); + return enumType.IsEnumDefined(value); + } + + public static string GetName(System.Type enumType, object value) + { + ThrowHelper.ThrowIfNull(enumType); + return enumType.GetEnumName(value); + } + + public static string[] GetNames(System.Type enumType) + { + ThrowHelper.ThrowIfNull(enumType); + return enumType.GetEnumNames(); + } + + public static System.Array GetValues(System.Type enumType) + { + ThrowHelper.ThrowIfNull(enumType); + return enumType.GetEnumValues(); } @@ -166,33 +245,21 @@ namespace system { if (format.Length == 1) { - bool asFlags = false; - switch (format[0]) + switch (Char.ToUpperInvariant(format[0])) { - case 'f': case 'F': - asFlags = true; - goto case 'G'; - - case 'g': case 'G': - if (! asFlags) - { - asFlags = ((java.lang.Class) typeof (EnumFlags)) - .isAssignableFrom(cls); - } - return FormatNames(cls, asFlags, value); - - case 'd': case 'D': - return java.lang.Long.toString(value); - - case 'x': case 'X': - return java.lang.Long.toHexString(value); - } + case 'F': return FormatNames(cls, value, true); + case 'G': return FormatNames(cls, value, + ((java.lang.Class) typeof (EnumFlags)) + .isAssignableFrom(cls)); + case 'D': return java.lang.Long.toString(value); + case 'X': return FormatHex(cls, value); + }; } throw new System.FormatException(); - static string FormatNames(java.lang.Class cls, bool asFlags, long v) + static string FormatNames(java.lang.Class cls, long v, bool asFlags) { var fields = cls.getDeclaredFields(); int n = fields.Length; @@ -252,10 +319,85 @@ namespace system return (v == 0) ? sb.ToString() : null; } + + static string FormatHex(java.lang.Class cls, long v) + { + var typeCode = System.Type.GetTypeCode( + system.RuntimeType.GetType(cls).GetEnumUnderlyingType()); + var hexfmt = typeCode switch + { + System.TypeCode.Boolean => "%02X", + System.TypeCode.Char => "%04X", + System.TypeCode.SByte => "%02X", + System.TypeCode.Byte => "%02X", + System.TypeCode.Int16 => "%04X", + System.TypeCode.UInt16 => "%04X", + System.TypeCode.Int32 => "%08X", + System.TypeCode.UInt32 => "%08X", + System.TypeCode.Int64 => "%016X", + System.TypeCode.UInt64 => "%016X", + _ => throw new System.InvalidOperationException() + }; + return java.lang.String.format( + hexfmt, new object[] { java.lang.Long.valueOf(v) }); + } } + // + // IConvertible + // + + bool System.IConvertible.ToBoolean(System.IFormatProvider provider) + => System.Convert.ToBoolean(GetLong(), CultureInfo.CurrentCulture); + + char System.IConvertible.ToChar(System.IFormatProvider provider) + => System.Convert.ToChar(GetLong(), CultureInfo.CurrentCulture); + + sbyte System.IConvertible.ToSByte(System.IFormatProvider provider) + => System.Convert.ToSByte(GetLong(), CultureInfo.CurrentCulture); + + byte System.IConvertible.ToByte(System.IFormatProvider provider) + => System.Convert.ToByte(GetLong(), CultureInfo.CurrentCulture); + + short System.IConvertible.ToInt16(System.IFormatProvider provider) + => System.Convert.ToInt16(GetLong(), CultureInfo.CurrentCulture); + + ushort System.IConvertible.ToUInt16(System.IFormatProvider provider) + => System.Convert.ToUInt16(GetLong(), CultureInfo.CurrentCulture); + + int System.IConvertible.ToInt32(System.IFormatProvider provider) + => System.Convert.ToInt32(GetLong(), CultureInfo.CurrentCulture); + + uint System.IConvertible.ToUInt32(System.IFormatProvider provider) + => System.Convert.ToUInt32(GetLong(), CultureInfo.CurrentCulture); + + long System.IConvertible.ToInt64(System.IFormatProvider provider) + => System.Convert.ToInt64(GetLong(), CultureInfo.CurrentCulture); + + ulong System.IConvertible.ToUInt64(System.IFormatProvider provider) + => System.Convert.ToUInt64(GetLong(), CultureInfo.CurrentCulture); + + float System.IConvertible.ToSingle(System.IFormatProvider provider) + => System.Convert.ToSingle(GetLong(), CultureInfo.CurrentCulture); + + double System.IConvertible.ToDouble(System.IFormatProvider provider) + => System.Convert.ToDouble(GetLong(), CultureInfo.CurrentCulture); + + decimal System.IConvertible.ToDecimal(System.IFormatProvider provider) + => System.Convert.ToDecimal(GetLong(), CultureInfo.CurrentCulture); + + System.DateTime System.IConvertible.ToDateTime(System.IFormatProvider provider) + => throw new System.InvalidCastException(); + + object System.IConvertible.ToType(System.Type type, System.IFormatProvider provider) + => system.Convert.DefaultToType((System.IConvertible) this, type, provider); + + // + // value methods + // + void ValueMethod.Clear() => SetLong(0L); void ValueMethod.CopyTo(ValueType into) => ((Enum) into).SetLong(GetLong()); ValueType ValueMethod.Clone() => (ValueType) this.MemberwiseClone(); diff --git a/Baselib/src/System/GenericType.cs b/Baselib/src/System/GenericType.cs index c632415..c3c83de 100644 --- a/Baselib/src/System/GenericType.cs +++ b/Baselib/src/System/GenericType.cs @@ -120,6 +120,12 @@ namespace system { if (genericObject.TryCast(castToType) != null) return obj; + if (obj is system.Array.ProxySyncRoot objArray) + { + var proxy = Array.GetProxy(objArray.SyncRoot, castToType, false); + if (proxy != null) + return proxy; + } } else { diff --git a/Baselib/src/System/Globalization/CultureInfo.cs b/Baselib/src/System/Globalization/CultureInfo.cs index f52de03..b3a706f 100644 --- a/Baselib/src/System/Globalization/CultureInfo.cs +++ b/Baselib/src/System/Globalization/CultureInfo.cs @@ -92,6 +92,9 @@ namespace system.globalization { public virtual CompareInfo CompareInfo => CompareInfoRef; + public static CultureInfo CurrentCulture + => system.threading.Thread.CurrentThread.CurrentCulture; + public static CultureInfo InvariantCulture => s_InvariantCultureInfo; public virtual object Clone() => throw new System.NotImplementedException(); diff --git a/Baselib/src/System/Int16.cs b/Baselib/src/System/Int16.cs index 4d9c780..9f9fbf9 100644 --- a/Baselib/src/System/Int16.cs +++ b/Baselib/src/System/Int16.cs @@ -3,6 +3,7 @@ namespace system { public class Int16 : system.ValueType, system.ValueMethod, + System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable { @@ -46,10 +47,32 @@ namespace system { if (string.IsNullOrEmpty(format)) return ToString(); - return ParseNumbers.FormatNumber( - (java.lang.String) (object) format, provider, java.lang.Integer.valueOf(Get())); + return ParseNumbers.FormatNumber((java.lang.String) (object) format, provider, + java.lang.Short.valueOf((short) Get())); } + public string ToString(string format) => ToString(format, null); + + public string ToString(System.IFormatProvider provider) => ToString(); + + // System.IComparable + public virtual int CompareTo(object obj) + { + if (object.ReferenceEquals(obj, null)) + return 1; + if (obj is Int16 objInt16) + return CompareTo((short) objInt16.Get()); + throw new System.ArgumentException(); + } + + // System.IComparable + public int CompareTo(short b) + { + int a = Get(); + return (a < b ? -1 : a > b ? 1 : 0); + } + + void ValueMethod.Clear() => Set(0); @@ -134,11 +157,9 @@ namespace system System.Decimal System.IConvertible.ToDecimal(System.IFormatProvider provider) => System.Convert.ToDecimal(Get()); System.DateTime System.IConvertible.ToDateTime(System.IFormatProvider provider) - => throw new System.InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo_Int32_DateTime")); - string System.IConvertible.ToString(System.IFormatProvider provider) - => ToString(); + => throw new System.InvalidCastException(); object System.IConvertible.ToType(System.Type type, System.IFormatProvider provider) - => null;//System.Convert.DefaultToType((System.IConvertible) this, type, provider); + => system.Convert.DefaultToType((System.IConvertible) this, type, provider); @@ -171,7 +192,7 @@ namespace system #pragma warning disable 0659 - public class UInt16 : Int16, System.IEquatable + public class UInt16 : Int16, System.IComparable, System.IEquatable { new public static UInt16 Box(int v) => new UInt16() { v = (short) v }; @@ -191,6 +212,17 @@ namespace system public override System.TypeCode GetTypeCode() => System.TypeCode.UInt16; + // System.IComparable + public override int CompareTo(object obj) + { + if (object.ReferenceEquals(obj, null)) + return 1; + if (obj is UInt16 objUInt16) + return CompareTo((ushort) objUInt16.Get()); + throw new System.ArgumentException(); + } + + // System.IComparable public int CompareTo(ushort v) => CompareTo((short) Get(), (short) v); public static int CompareTo(short a, short b) diff --git a/Baselib/src/System/Int32.cs b/Baselib/src/System/Int32.cs index 12306ca..54f62ec 100644 --- a/Baselib/src/System/Int32.cs +++ b/Baselib/src/System/Int32.cs @@ -3,6 +3,7 @@ namespace system { public class Int32 : system.ValueType, system.ValueMethod, + System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable { @@ -49,14 +50,31 @@ namespace system { if (string.IsNullOrEmpty(format)) return ToString(); - return ParseNumbers.FormatNumber( - (java.lang.String) (object) format, provider, java.lang.Integer.valueOf(Get())); + return ParseNumbers.FormatNumber((java.lang.String) (object) format, provider, + java.lang.Integer.valueOf(Get())); } public string ToString(string format) => ToString(format, null); public string ToString(System.IFormatProvider provider) => ToString(); + // System.IComparable + public virtual int CompareTo(object obj) + { + if (object.ReferenceEquals(obj, null)) + return 1; + if (obj is Int32 objInt32) + return CompareTo((int) objInt32.Get()); + throw new System.ArgumentException(); + } + + // System.IComparable + public int CompareTo(int b) + { + int a = Get(); + return (a < b ? -1 : a > b ? 1 : 0); + } + public static int OverflowAdd(int a, int b) @@ -189,11 +207,9 @@ namespace system System.Decimal System.IConvertible.ToDecimal(System.IFormatProvider provider) => System.Convert.ToDecimal(Get()); System.DateTime System.IConvertible.ToDateTime(System.IFormatProvider provider) - => throw new System.InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo_Int32_DateTime")); - string System.IConvertible.ToString(System.IFormatProvider provider) - => ToString(); + => throw new System.InvalidCastException(); object System.IConvertible.ToType(System.Type type, System.IFormatProvider provider) - => null;//System.Convert.DefaultToType((System.IConvertible) this, type, provider); + => system.Convert.DefaultToType((System.IConvertible) this, type, provider); @@ -231,7 +247,7 @@ namespace system #pragma warning disable 0659 - public class UInt32 : Int32, System.IEquatable + public class UInt32 : Int32, System.IComparable, System.IEquatable { new public static UInt32 Box(int v) => new UInt32() { v = v }; @@ -254,8 +270,17 @@ namespace system public override System.TypeCode GetTypeCode() => System.TypeCode.UInt32; - //public int CompareTo(uint v) => java.lang.Integer.compareUnsigned(Get(), (int) v); + // System.IComparable + public override int CompareTo(object obj) + { + if (object.ReferenceEquals(obj, null)) + return 1; + if (obj is UInt32 objUInt32) + return CompareTo((uint) objUInt32.Get()); + throw new System.ArgumentException(); + } + // System.IComparable public int CompareTo(uint v) => CompareTo(Get(), (int) v); public static int CompareTo(int a, int b) diff --git a/Baselib/src/System/Int64.cs b/Baselib/src/System/Int64.cs index 8cf23d9..c1ab858 100644 --- a/Baselib/src/System/Int64.cs +++ b/Baselib/src/System/Int64.cs @@ -2,7 +2,8 @@ namespace system { - public class Int64 : system.ValueType, system.ValueMethod, java.lang.Cloneable + public class Int64 : system.ValueType, system.ValueMethod, + System.IConvertible, System.IEquatable, System.IFormattable { [java.attr.RetainType] protected long v; @@ -23,6 +24,9 @@ namespace system public static void Set(long v, Int64 o) => o.Set(v); public static void VolatileSet(long v, Int64 o) => o.VolatileSet(v); + public virtual bool CompareAndSwap(long expect, long update) => + Util.JavaUnsafe.compareAndSwapLong(this, ValueOffset, expect, update); + public override bool Equals(object obj) @@ -37,11 +41,28 @@ namespace system return ((int) v) ^ (int) (v >> 32); } - public override string ToString() + public override string ToString() => java.lang.Long.toString(Get()); + + + + // System.IEquatable + public bool Equals(long v) => v == Get(); + + // System.IFormattable + public string ToString(string format, System.IFormatProvider provider) { - return java.lang.Long.toString(Get()); + if (string.IsNullOrEmpty(format)) + return ToString(); + return ParseNumbers.FormatNumber((java.lang.String) (object) format, provider, + java.lang.Long.valueOf(Get())); } + public string ToString(string format) => ToString(format, null); + + public string ToString(System.IFormatProvider provider) => ToString(); + + + public static long OverflowAdd(long a, long b) { long c = a + b; @@ -123,6 +144,47 @@ namespace system + // + // IConvertible + // + + + + public virtual System.TypeCode GetTypeCode() => System.TypeCode.Int64; + + bool System.IConvertible.ToBoolean(System.IFormatProvider provider) + => System.Convert.ToBoolean(Get()); + char System.IConvertible.ToChar(System.IFormatProvider provider) + => System.Convert.ToChar(Get()); + sbyte System.IConvertible.ToSByte(System.IFormatProvider provider) + => System.Convert.ToSByte(Get()); + byte System.IConvertible.ToByte(System.IFormatProvider provider) + => System.Convert.ToByte(Get()); + short System.IConvertible.ToInt16(System.IFormatProvider provider) + => System.Convert.ToInt16(Get()); + ushort System.IConvertible.ToUInt16(System.IFormatProvider provider) + => System.Convert.ToUInt16(Get()); + int System.IConvertible.ToInt32(System.IFormatProvider provider) + => System.Convert.ToInt32(Get()); + uint System.IConvertible.ToUInt32(System.IFormatProvider provider) + => System.Convert.ToUInt32(Get()); + long System.IConvertible.ToInt64(System.IFormatProvider provider) + => System.Convert.ToInt64(Get()); + ulong System.IConvertible.ToUInt64(System.IFormatProvider provider) + => System.Convert.ToUInt64(Get()); + float System.IConvertible.ToSingle(System.IFormatProvider provider) + => System.Convert.ToSingle(Get()); + double System.IConvertible.ToDouble(System.IFormatProvider provider) + => System.Convert.ToDouble(Get()); + System.Decimal System.IConvertible.ToDecimal(System.IFormatProvider provider) + => System.Convert.ToDecimal(Get()); + System.DateTime System.IConvertible.ToDateTime(System.IFormatProvider provider) + => throw new System.InvalidCastException(); + object System.IConvertible.ToType(System.Type type, System.IFormatProvider provider) + => system.Convert.DefaultToType((System.IConvertible) this, type, provider); + + + // // InArray // @@ -147,6 +209,9 @@ namespace system public override void Set(long v) => a[i] = v; public override void VolatileSet(long v) => Util.JavaUnsafe.putLongVolatile(a, Util.ElementOffset64(i), v); + + public override bool CompareAndSwap(long expect, long update) => + Util.JavaUnsafe.compareAndSwapLong(a, Util.ElementOffset64(i), expect, update); } } @@ -167,7 +232,7 @@ namespace system #pragma warning disable 0659 - public class UInt64 : Int64 + public class UInt64 : Int64, System.IEquatable { new public static UInt64 Box(long v) => new UInt64() { v = v }; @@ -176,12 +241,20 @@ namespace system public static void Set(long v, UInt64 o) => o.Set(v); public static void VolatileSet(long v, UInt64 o) => o.VolatileSet(v); + public override bool CompareAndSwap(long expect, long update) => + throw new System.NotSupportedException(); + public override bool Equals(object obj) { var objUInt64 = obj as UInt64; return (objUInt64 != null && objUInt64.Get() == Get()); } + // System.IEquatable + public bool Equals(ulong v) => v == (ulong) Get(); + + public override System.TypeCode GetTypeCode() => System.TypeCode.UInt64; + //public int CompareTo(ulong v) => java.lang.Long.compareUnsigned(Get(), (long) v); public int CompareTo(ulong v) => CompareTo(Get(), (long) v); diff --git a/Baselib/src/System/Reflection/BindingFlagsIterator.cs b/Baselib/src/System/Reflection/BindingFlagsIterator.cs index db96ded..30c658b 100644 --- a/Baselib/src/System/Reflection/BindingFlagsIterator.cs +++ b/Baselib/src/System/Reflection/BindingFlagsIterator.cs @@ -14,6 +14,10 @@ namespace system.reflection MemberTypes memberType, System.Predicate callback) { + // + // calculate modifier AND mask and result for matches + // + int modifierMask = 0; int modifierValue = 0; @@ -43,6 +47,10 @@ namespace system.reflection } bindingAttr &= ~chk; + // + // check if iteration is required on base hierarchy + // + RuntimeType stopAtType = null; if ((bindingAttr & BindingFlags.DeclaredOnly) != 0) { @@ -50,6 +58,10 @@ namespace system.reflection bindingAttr &= ~BindingFlags.DeclaredOnly; } + // + // run iteration + // + if (bindingAttr != 0) { throw new System.PlatformNotSupportedException( diff --git a/Baselib/src/System/Reflection/RuntimeFieldInfo.cs b/Baselib/src/System/Reflection/RuntimeFieldInfo.cs index 3d9c688..6145122 100644 --- a/Baselib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/Baselib/src/System/Reflection/RuntimeFieldInfo.cs @@ -25,7 +25,7 @@ namespace system.reflection } // - // GetFields (called by system.RuntimeType.GetFields(GetFields) + // GetFields (called by system.RuntimeType.GetFields() // public static FieldInfo[] GetFields(BindingFlags bindingAttr, RuntimeType initialType) @@ -35,8 +35,9 @@ namespace system.reflection BindingFlagsIterator.Run(bindingAttr, initialType, MemberTypes.Field, (javaAccessibleObject) => { - list.Add(new RuntimeFieldInfo((java.lang.reflect.Field) javaAccessibleObject, - initialType)); + var javaField = (java.lang.reflect.Field) javaAccessibleObject; + javaField.setAccessible(true); + list.Add(new RuntimeFieldInfo(javaField, initialType)); return true; }); @@ -66,8 +67,35 @@ namespace system.reflection public override System.Type ReflectedType => throw new PlatformNotSupportedException(); - public override string Name - => throw new PlatformNotSupportedException(); + public override string Name => JavaField.getName(); + + public override object GetRawConstantValue() + { + if ((JavaField.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) + { + 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()); + } + } + throw new System.NotSupportedException(); + } public override System.RuntimeFieldHandle FieldHandle => throw new PlatformNotSupportedException(); diff --git a/Baselib/src/System/Reflection/RuntimeMethodInfo.cs b/Baselib/src/System/Reflection/RuntimeMethodInfo.cs index fef36f0..7b36525 100644 --- a/Baselib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/Baselib/src/System/Reflection/RuntimeMethodInfo.cs @@ -52,92 +52,42 @@ namespace system.reflection // calculate modifier AND mask and result for matches // - int modifierMask = 0; - int modifierValue = 0; + RuntimeMethodInfo foundMethod = null; - BindingFlags chk = bindingAttr & (BindingFlags.Public | BindingFlags.NonPublic); - if (chk != (BindingFlags.Public | BindingFlags.NonPublic)) - { - if (chk == 0) // if neither, no methods will match - return null; - // methods with internal access are converted to public access, - // so we cannot honor the distinction between Public and NonPublic - /* - modifierMask |= java.lang.reflect.Modifier.PUBLIC; - if (chk == BindingFlags.Public) - modifierValue |= java.lang.reflect.Modifier.PUBLIC; - */ - bindingAttr &= ~chk; - } - - chk = bindingAttr & (BindingFlags.Static | BindingFlags.Instance); - if (chk != (BindingFlags.Static | BindingFlags.Instance)) - { - if (chk == 0) // if neither, no methods will match - return null; - modifierMask |= java.lang.reflect.Modifier.STATIC; - if (chk == BindingFlags.Static) - modifierValue |= java.lang.reflect.Modifier.STATIC; - bindingAttr &= ~chk; - } - - RuntimeType stopAtType = null; - if ((bindingAttr & BindingFlags.DeclaredOnly) != 0) - { - stopAtType = initialType; - bindingAttr &= ~BindingFlags.DeclaredOnly; - } - - if (bindingAttr != 0) - throw new PlatformNotSupportedException("bad binding flags " + bindingAttr); - - // - // collect methods from class and base classes - // - - var currentType = initialType; - for (;;) + BindingFlagsIterator.Run(bindingAttr, initialType, MemberTypes.Method, + (javaAccessibleObject) => { #pragma warning disable 0436 - java.lang.reflect.Method[] javaMethods = - (java.lang.reflect.Method[]) (object) - currentType.JavaClassForArray().getDeclaredMethods(); + var javaMethod = (java.lang.reflect.Method) javaAccessibleObject; #pragma warning restore 0436 - foreach (var javaMethod in javaMethods) - { - int jmodifiers = javaMethod.getModifiers(); - if ((jmodifiers & modifierMask) == modifierValue) - { - string originalName = javaMethod.getName(); - // note the actual suffix character below is configured - // in CilMain.cs, with special considerations for Android. - // we list all possible characters here, just in case. - int idx = originalName.IndexOf('\u00AB'); // U+00AB Left-Pointing Double Angle Quotation Mark - if (idx == -1) - idx = originalName.IndexOf('\u00A1'); // U+00A1 Inverted Exclamation Mark - if (idx == -1) - idx = originalName.IndexOf('('); - if (idx == -1) - idx = originalName.IndexOf('!'); - var compareName = - (idx == -1) ? originalName : originalName.Substring(0, idx); + string originalName = javaMethod.getName(); + // note the actual suffix character below is configured + // in CilMain.cs, with special considerations for Android. + // we list all possible characters here, just in case. + int idx = originalName.IndexOf('\u00AB'); // U+00AB Left-Pointing Double Angle Quotation Mark + if (idx == -1) + idx = originalName.IndexOf('\u00A1'); // U+00A1 Inverted Exclamation Mark + if (idx == -1) + idx = originalName.IndexOf('('); + if (idx == -1) + idx = originalName.IndexOf('!'); + var compareName = + (idx == -1) ? originalName : originalName.Substring(0, idx); - if (name == compareName) - { - javaMethod.setAccessible(true); - return new RuntimeMethodInfo(javaMethod, jmodifiers, initialType, - originalName, compareName); - } - } + if (name == compareName) + { + javaMethod.setAccessible(true); + var jmodifiers = javaMethod.getModifiers(); + foundMethod = new RuntimeMethodInfo(javaMethod, jmodifiers, initialType, + originalName, compareName); + return false; // stop iteration } - currentType = (system.RuntimeType) currentType.BaseType; - if (currentType == stopAtType) - break; - } + return true; // continue iteration + }); - return null; + return foundMethod; } // diff --git a/Baselib/src/System/Reflection/RuntimeModule.cs b/Baselib/src/System/Reflection/RuntimeModule.cs index 50ca7c1..8b40feb 100644 --- a/Baselib/src/System/Reflection/RuntimeModule.cs +++ b/Baselib/src/System/Reflection/RuntimeModule.cs @@ -4,6 +4,8 @@ using System.Runtime.InteropServices; namespace system.reflection { + public abstract class Module { } + public sealed class RuntimeModule { diff --git a/Baselib/src/System/RuntimeType.cs b/Baselib/src/System/RuntimeType.cs index 2efbfdb..e34bea1 100644 --- a/Baselib/src/System/RuntimeType.cs +++ b/Baselib/src/System/RuntimeType.cs @@ -1433,6 +1433,54 @@ namespace system + #pragma warning disable 0436 + public override System.Array GetEnumValues() + { + // the default implementation of System.Type::GetEnumValues() + // throws NotImplementedException, so we have to use reflection + // to invoke the underlying private method that does the work + + if (! IsEnum) + throw new ArgumentException(); + + // the default implementation of System.Type::GetEnumValues() + // throws NotImplementedException, so we have to use reflection + // to invoke the underlying private method that does the work + + if (GetEnumRawConstantValues == null) + { + var searchClass = (java.lang.Class) typeof(System.Type); + var modifierMask = java.lang.reflect.Modifier.PRIVATE + | java.lang.reflect.Modifier.FINAL + | java.lang.reflect.Modifier.STATIC; + var modifierValue = java.lang.reflect.Modifier.PRIVATE + | java.lang.reflect.Modifier.FINAL; + + foreach (java.lang.reflect.Method method in + (java.lang.reflect.Method[]) (object) searchClass.getDeclaredMethods()) + { + if ( (method.getModifiers() & modifierMask) == modifierValue + && method.getParameterTypes().Length == 0 + && method.getReturnType() == (java.lang.Class) typeof(System.Array)) + { + method.setAccessible(true); + GetEnumRawConstantValues = method; + } + } + } + + if (GetEnumRawConstantValues != null) + { + return (System.Array) GetEnumRawConstantValues.invoke(this, null); + } + + throw new InvalidOperationException(); + } + [java.attr.RetainType] private static java.lang.reflect.Method GetEnumRawConstantValues; + #pragma warning restore 0436 + + + // // Reflection on members of the type // @@ -1531,8 +1579,6 @@ namespace system public virtual int GenericParameterPosition public virtual Type[] GetGenericParameterConstraints() - public virtual Array GetEnumValues() - internal virtual string FormatTypeName(bool serialization) public virtual InterfaceMapping GetInterfaceMap(Type interfaceType) */ diff --git a/Baselib/src/System/Span.cs b/Baselib/src/System/Span.cs index 4aad1e0..9652eac 100644 --- a/Baselib/src/System/Span.cs +++ b/Baselib/src/System/Span.cs @@ -46,7 +46,11 @@ namespace system count = count >> shift; this.array.Set(array = java.lang.reflect.Array.newInstance(cls, count)); - + if (! cls.isPrimitive()) + { + for (int i = 0; i < count; i++) + java.lang.reflect.Array.set(array, i, cls.newInstance()); + } } return array; } @@ -63,22 +67,14 @@ namespace system } [java.attr.RetainName] - public static Span Localloc(long bytes) + public static Span Localloc(long bytes) { // this helper method is invoked by code which uses the 'localloc' // instruction. see also CodeSpan::Localloc method. int intBytes = (int) bytes; if (intBytes != bytes) throw new System.ArgumentOutOfRangeException(); - return new Span() { array = new Reference(), count = intBytes }; - } - - [java.attr.RetainName] - public static Span String(java.lang.String str) - { - // this helper method is invoked by code which stores a string - // variable into a pointer. see also CodeSpan::Address method. - return new Span(str.toCharArray()) { count = System.SByte.MinValue }; + return new Span() { array = new Reference(), count = intBytes }; } [java.attr.RetainName] @@ -115,6 +111,21 @@ namespace system return system.Array.Box(Array(null), start); } + // + // Assign + // + // this helper method is invoked by code which stores an address into + // a pointer. see also CodeSpan::Address method. + // + + [java.attr.RetainName] + public static Span Assign(java.lang.String str) + => new Span(str.toCharArray()) { count = System.SByte.MinValue }; + + [java.attr.RetainName] + public static System.ValueType Assign(ValueType source) + => new Span((T[]) (object) (new ValueType[1] { source })); + // // helper methods to access a span of a primitive type. // see also CodeSpan::LoadStore method. @@ -150,8 +161,24 @@ namespace system public double LoadD() => ((double[]) Array(java.lang.Double.TYPE))[start]; public void StoreD(double value) => ((double[]) Array(java.lang.Double.TYPE))[start] = value; - public object Load() => ((object[]) Array((java.lang.Class) typeof(object)))[start]; - public void Store(object value) => ((object[]) Array((java.lang.Class) typeof(object)))[start] = value; + // + // helper methods to access a span of a value type. + // see also CodeSpan::LoadStore method. + // + + public system.ValueType Load(java.lang.Class cls) + => (system.ValueType) java.lang.reflect.Array.get(Array(cls), start); + + public void Store(ValueType value, java.lang.Class cls) + => ((ValueMethod) value).CopyTo(Load(cls)); + + public void Clear() + { + if (this.array != null && this.array.Get() != null) + { + ((ValueMethod) Load(null)).Clear(); + } + } // // System.Span methods diff --git a/Baselib/src/System/ValueType.cs b/Baselib/src/System/ValueType.cs index c7e7265..47c658f 100644 --- a/Baselib/src/System/ValueType.cs +++ b/Baselib/src/System/ValueType.cs @@ -91,4 +91,14 @@ namespace system ValueType Clone(); } + + + [java.attr.Discard] // discard in output + public static class Convert + { + // called by various implementations of System.IConvertible.ToType + [java.attr.Discard] extern public static object DefaultToType( + System.IConvertible value, System.Type targetType, System.IFormatProvider provider); + } + } diff --git a/CilToJava/src/CilMain.cs b/CilToJava/src/CilMain.cs index 9b49875..8061d41 100644 --- a/CilToJava/src/CilMain.cs +++ b/CilToJava/src/CilMain.cs @@ -110,6 +110,20 @@ namespace SpaceFlint.CilToJava + internal static void MakeRoomForCategory2ValueOnStack(JavaCode code) + { + // ensure the stack has enough space for a category-2 value + if (code.StackMap != null) + { + code.StackMap.PushStack(JavaType.IntegerType); + code.StackMap.PushStack(JavaType.LongType); + code.StackMap.PopStack(CilMain.Where); + code.StackMap.PopStack(CilMain.Where); + } + } + + + public static List Import(List cilTypes) { if (_Where != null) diff --git a/CilToJava/src/CilType.cs b/CilToJava/src/CilType.cs index 204e012..e086d42 100644 --- a/CilToJava/src/CilType.cs +++ b/CilToJava/src/CilType.cs @@ -160,7 +160,7 @@ namespace SpaceFlint.CilToJava if (defType.HasFields && numGeneric == 0) { var type = From(defType.Fields[0].FieldType); - if ((! type.IsReference) && type.PrimitiveType >= TypeCode.SByte + if ((! type.IsReference) && type.PrimitiveType >= TypeCode.Boolean && type.PrimitiveType <= TypeCode.UInt64) { JavaName = ImportName(defType, 0); @@ -733,10 +733,14 @@ namespace SpaceFlint.CilToJava private string VolatileName(string nm, bool isVolatile) => ((UnboxedType.IsVolatile || isVolatile) ? ("Volatile" + nm) : nm); - public virtual void GetValue(JavaCode code, bool isVolatile = false) => + public virtual void GetValue(JavaCode code, bool isVolatile = false) + { code.NewInstruction(0xB6 /* invokevirtual */, ThisOrEnum, new JavaMethodRef(VolatileName("Get", isVolatile), UnboxedTypeInMethod)); + if (UnboxedTypeInMethod.Category == 2) + CilMain.MakeRoomForCategory2ValueOnStack(code); + } public virtual void SetValueOV(JavaCode code, bool isVolatile = false) => code.NewInstruction(0xB6 /* invokevirtual */, ThisOrEnum, @@ -783,6 +787,8 @@ namespace SpaceFlint.CilToJava var innerOrEnum = GetInnerObject(code); code.NewInstruction(0xB6 /* invokevirtual */, innerOrEnum, new JavaMethodRef("Get", UnboxedTypeInMethod)); + if (UnboxedTypeInMethod.Category == 2) + CilMain.MakeRoomForCategory2ValueOnStack(code); } public override void SetValueOV(JavaCode code, bool isVolatile = false) diff --git a/CilToJava/src/CodeArray.cs b/CilToJava/src/CodeArray.cs index 441253a..770cd3c 100644 --- a/CilToJava/src/CodeArray.cs +++ b/CilToJava/src/CodeArray.cs @@ -521,8 +521,7 @@ namespace SpaceFlint.CilToJava - public static bool MaybeGetProxy(CilType fromType, CilType intoType, - JavaCode code, bool pushFromType = false) + public static bool MaybeGetProxy(CilType fromType, CilType intoType, JavaCode code) { if ( fromType.IsArray || object.ReferenceEquals(fromType, GenericArrayType) diff --git a/CilToJava/src/CodeCompare.cs b/CilToJava/src/CodeCompare.cs index 14d94b2..27264ae 100644 --- a/CilToJava/src/CodeCompare.cs +++ b/CilToJava/src/CodeCompare.cs @@ -237,7 +237,7 @@ namespace SpaceFlint.CilToJava } else { - return TestGtLt(code, stackTop, + return TestGtLt(code, stackTop, stackTop2, /* if greater than */ ( cilOp == Code.Bgt /* (vs less than) */ || cilOp == Code.Bgt_S || cilOp == Code.Bgt_Un @@ -286,7 +286,7 @@ namespace SpaceFlint.CilToJava byte op = (cilOp == Code.Ceq) ? TestEq(code, stackTop, stackTop2, cilInst) - : TestGtLt(code, stackTop, + : TestGtLt(code, stackTop, stackTop2, /* if greater than */ ( cilOp == Code.Cgt /* (vs less than) */ || cilOp == Code.Cgt_Un), /* if unsigned */ ( cilOp == Code.Cgt_Un @@ -326,7 +326,7 @@ namespace SpaceFlint.CilToJava { if (stackTop.IsReference || stackTop2.IsReference) { - CodeSpan.Compare(stackTop, stackTop2, cilInst, code); + CodeSpan.CompareEq(stackTop, stackTop2, cilInst, code); return 0xA5; // if_acmpeq (reference) } @@ -373,7 +373,8 @@ namespace SpaceFlint.CilToJava - static byte TestGtLt(JavaCode code, JavaType stackTop, bool greater, bool unsigned_unordered) + static byte TestGtLt(JavaCode code, JavaType stackTop, JavaType stackTop2, + bool greater, bool unsigned_unordered) { byte op; @@ -444,7 +445,15 @@ namespace SpaceFlint.CilToJava return 0xA6; // if_acmpne } else + { + if (CodeSpan.CompareGtLt(stackTop, stackTop2, code)) + { + return (byte) (greater ? 0x9D // ifgt + : 0x9B); // iflt + } + throw new InvalidProgramException(); + } if (typeBits != 0) typeName = "UInt" + typeBits.ToString(); diff --git a/CilToJava/src/CodeMisc.cs b/CilToJava/src/CodeMisc.cs index 516d770..3432d9c 100644 --- a/CilToJava/src/CodeMisc.cs +++ b/CilToJava/src/CodeMisc.cs @@ -140,7 +140,7 @@ namespace SpaceFlint.CilToJava var dataType = CilType.From(typeRef); var fromType = (CilType) code.StackMap.PopStack(CilMain.Where); - if (CodeSpan.LoadStore(true, fromType, null, code)) + if (CodeSpan.LoadStore(true, fromType, null, dataType, code)) return; if ( (! dataType.IsReference) && cilOp == Code.Ldobj @@ -265,14 +265,15 @@ namespace SpaceFlint.CilToJava { if (data is TypeReference typeRef) { + var dataType = CilType.From(typeRef); + var stackTop1 = (CilType) code.StackMap.PopStack(CilMain.Where); var stackTop2 = (CilType) code.StackMap.PopStack(CilMain.Where); - if (CodeSpan.LoadStore(false, stackTop2, null, code)) + if (CodeSpan.LoadStore(false, stackTop2, null, dataType, code)) return; code.StackMap.PushStack(stackTop2); code.StackMap.PushStack(stackTop1); - var dataType = CilType.From(typeRef); GenericUtil.ValueCopy(dataType, code, true); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); @@ -290,6 +291,9 @@ namespace SpaceFlint.CilToJava var dataType = CilType.From(typeRef); var fromType = code.StackMap.PopStack(CilMain.Where); + if (CodeSpan.Clear(fromType, dataType, code)) + return; + if ( dataType.IsGenericParameter || (dataType.IsValueClass && dataType.Equals(fromType))) { diff --git a/CilToJava/src/CodeNumber.cs b/CilToJava/src/CodeNumber.cs index a3082af..14f5aed 100644 --- a/CilToJava/src/CodeNumber.cs +++ b/CilToJava/src/CodeNumber.cs @@ -89,7 +89,10 @@ namespace SpaceFlint.CilToJava return 0x00; // nop if (oldType == TypeCode.Single) + { + CilMain.MakeRoomForCategory2ValueOnStack(code); return 0x8D; // f2d + } if (oldType == TypeCode.Int64) return 0x8A; // l2d @@ -113,6 +116,8 @@ namespace SpaceFlint.CilToJava bool fromInt32 = (oldType != TypeCode.Int64 && oldType != TypeCode.UInt64); if (fromInt32) { + CilMain.MakeRoomForCategory2ValueOnStack(code); + code.NewInstruction(0x85 /* i2l */, null, null); code.StackMap.PushStack(JavaType.LongType); @@ -176,6 +181,7 @@ namespace SpaceFlint.CilToJava return 0x7F; // land } + CilMain.MakeRoomForCategory2ValueOnStack(code); return 0x85; // i2l } @@ -285,6 +291,8 @@ namespace SpaceFlint.CilToJava public static void Calculation(JavaCode code, Code cilOp) { var stackTop1 = code.StackMap.PopStack(CilMain.Where); + if (cilOp == Code.Sub && CodeSpan.SubOffset(stackTop1, code)) + return; var type1 = GetNumericTypeCode(stackTop1); if (cilOp == Code.Not) @@ -300,7 +308,7 @@ namespace SpaceFlint.CilToJava else { var stackTop2 = code.StackMap.PopStack(CilMain.Where); - if (CodeSpan.AddOffset(stackTop1, stackTop2, code)) + if (cilOp == Code.Add && CodeSpan.AddOffset(stackTop1, stackTop2, code)) return; char kind; @@ -547,7 +555,7 @@ namespace SpaceFlint.CilToJava var boxedType = stackTop as BoxedType; if (boxedType == null || boxedType.IsBoxedReference != isRef) { - if (CodeSpan.LoadStore(isLoad, stackTop, opcodeType, code)) + if (CodeSpan.LoadStore(isLoad, stackTop, opcodeType, null, code)) return; if (object.ReferenceEquals(stackTop, CodeArrays.GenericArrayType)) @@ -590,7 +598,13 @@ namespace SpaceFlint.CilToJava code.StackMap.PushStack(unboxedType); } else + { + // if we are storing a real array into a boxed reference of + // e.g., system.Array, then we have to create an array proxy + CodeArrays.MaybeGetProxy(CodeArrays.GenericArrayType, unboxedType, code); + boxedType.SetValueOV(code); + } return; } diff --git a/CilToJava/src/CodeSpan.cs b/CilToJava/src/CodeSpan.cs index 74d49d5..a2d780b 100644 --- a/CilToJava/src/CodeSpan.cs +++ b/CilToJava/src/CodeSpan.cs @@ -105,6 +105,46 @@ namespace SpaceFlint.CilToJava } + public static bool SubOffset(JavaType secondType, JavaCode code) + { + // make sure the first operand is a pointer span, not a real Span + var spanType1 = (CilType) code.StackMap.PopStack(CilMain.Where); + if ( (! spanType1.HasGenericParameters) + && spanType1.GenericParameters != null + && spanType1.GenericParameters[0] is CilType span1PointerType + && (! span1PointerType.IsGenericParameter)) + { + var spanType2 = (CilType) secondType; + + // check if subtracting two pointer spans + if ( (! spanType2.HasGenericParameters) + && spanType2.GenericParameters != null + && spanType2.GenericParameters[0] is CilType span2PointerType + && (! span2PointerType.IsGenericParameter)) + { + code.NewInstruction(0xB6 /* invokevirtual */, SpanType, + new JavaMethodRef("Subtract", + JavaType.LongType, spanType2)); + code.StackMap.PushStack(CilType.From(JavaType.LongType)); + return true; + } + + // check if subtracting an offset from a pointer span + if (spanType2.Equals(JavaType.IntegerType)) + { + code.NewInstruction(0xB6 /* invokevirtual */, SpanType, + new JavaMethodRef("Subtract", + SpanType, spanType2)); + code.StackMap.PushStack(SpanType); + return true; + } + } + + code.StackMap.PushStack(spanType1); + return false; + } + + public static bool Box(CilType intoType, JavaType spanType, JavaCode code) { @@ -122,42 +162,87 @@ namespace SpaceFlint.CilToJava public static bool Address(CilType fromType, CilType intoType, JavaCode code) { - if (intoType.Equals(CodeSpan.SpanType) && (! fromType.Equals(CodeSpan.SpanType))) + if (intoType.Equals(SpanType) && (! fromType.Equals(SpanType))) { - if ( fromType.Equals(JavaType.StringType) - && intoType.GenericParameters != null - && intoType.GenericParameters[0].Equals(fromType)) + // allow assignment of null to clear the pointer + if (fromType.Equals(JavaStackMap.Null)) + return true; + + // allow assignment of native int (presumably zero) + bool callAssign = false; + bool pushNullType = true; + JavaType argType = fromType; + JavaType retType = SpanType; + + if ((! fromType.IsReference) && fromType.PrimitiveType == TypeCode.UInt64) + callAssign = true; + else if (intoType.GenericParameters != null) { - code.NewInstruction(0x01 /* aconst_null */, null, null); - code.StackMap.PushStack(CilType.SystemTypeType); + // allow assignment when the types match + callAssign = intoType.GenericParameters[0].Equals(fromType) + || fromType.JavaName == intoType.GenericParameters[0].JavaName; + + // for arbitrary value types, call a Assign(ValueType) + if (fromType.IsValueClass) + { + argType = retType = CilType.SystemValueType; + GenericUtil.LoadMaybeGeneric(fromType, code); + pushNullType = false; + } + } + + if (callAssign) + { + if (pushNullType) + { + code.NewInstruction(0x01 /* aconst_null */, null, null); + code.StackMap.PushStack(CilType.SystemTypeType); + } code.NewInstruction(0xB8 /* invokestatic */, SpanType, - new JavaMethodRef("String" + CilMain.EXCLAMATION, - SpanType, JavaType.StringType, CilType.SystemTypeType)); + new JavaMethodRef("Assign" + CilMain.EXCLAMATION, + retType, argType, CilType.SystemTypeType)); + + code.NewInstruction(0xC0 /* checkcast */, SpanType, null); code.StackMap.PopStack(CilMain.Where); // null type return true; } - if (fromType.Equals(JavaStackMap.Null)) - return true; - throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer"); + throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer of '{intoType.GenericParameters[0].JavaName}'"); } return false; } - public static bool LoadStore(bool isLoad, CilType stackTop, JavaType opcodeType, JavaCode code) + public static bool LoadStore(bool isLoad, CilType stackTop, JavaType opcodeType, + CilType dataType, JavaCode code) { if (stackTop.Equals(SpanType) && code.Method.Class.Name != SpanType.ClassName) { string opcodeDescr; if (opcodeType == null) { - opcodeType = JavaType.ObjectType; + opcodeType = CilType.SystemValueType; opcodeDescr = ""; + + // at this point we should have been called from LoadObject or + // StoreObject in CodeMisc to handle a ldobj/stobj instruction, + // so make sure the pointer-span element is a value type + if (dataType.IsGenericParameter || (! dataType.IsValueClass)) + throw new InvalidProgramException(); + code.NewInstruction(0x12 /* ldc */, dataType.AsWritableClass, null); + + // make sure the stack has room for three parameters: + // 'this', value reference (in case of Store), and class + code.StackMap.PushStack(JavaType.ObjectType); + code.StackMap.PushStack(JavaType.ObjectType); + code.StackMap.PushStack(JavaType.ObjectType); + code.StackMap.PopStack(CilMain.Where); + code.StackMap.PopStack(CilMain.Where); + code.StackMap.PopStack(CilMain.Where); } else { @@ -175,6 +260,8 @@ namespace SpaceFlint.CilToJava var spanMethod = isLoad ? (new JavaMethodRef("Load" + opcodeDescr, opcodeType)) : (new JavaMethodRef("Store" + opcodeDescr, voidType, opcodeType)); + if (opcodeDescr == "") + spanMethod.Parameters.Add(new JavaFieldRef("", JavaType.ClassType)); code.NewInstruction(0xB6 /* invokevirtual */, SpanType, spanMethod); if (isLoad) code.StackMap.PushStack(CilType.From(opcodeType)); @@ -186,10 +273,26 @@ namespace SpaceFlint.CilToJava - public static void Compare(JavaType stackTop, JavaType stackTop2, - Mono.Cecil.Cil.Instruction cilInst, JavaCode code) + public static bool Clear(JavaType stackTop, CilType dataType, JavaCode code) { - if ( stackTop.Equals(CodeSpan.SpanType) + if ( stackTop.Equals(SpanType) + && dataType.IsValueClass + && code.Method.Class.Name != SpanType.ClassName) + { + // if initobj is called on a span or pointer, call Span.Clear() + code.NewInstruction(0xB6 /* invokevirtual */, SpanType, + new JavaMethodRef("Clear", JavaType.VoidType)); + return true; + } + return false; + } + + + + public static void CompareEq(JavaType stackTop, JavaType stackTop2, + Mono.Cecil.Cil.Instruction cilInst, JavaCode code) + { + if ( stackTop.Equals(SpanType) && ( stackTop2.PrimitiveType == TypeCode.Int64 || stackTop2.PrimitiveType == TypeCode.UInt64)) { @@ -197,7 +300,7 @@ namespace SpaceFlint.CilToJava throw new InvalidProgramException(); } - if ( stackTop2.Equals(CodeSpan.SpanType) + if ( stackTop2.Equals(SpanType) && ( stackTop.PrimitiveType == TypeCode.Int64 || stackTop.PrimitiveType == TypeCode.UInt64)) { @@ -222,6 +325,26 @@ namespace SpaceFlint.CilToJava + public static bool CompareGtLt(JavaType stackTop, JavaType stackTop2, JavaCode code) + { + if (stackTop.Equals(SpanType) && stackTop2.Equals(SpanType)) + { + code.NewInstruction(0x01 /* aconst_null */, null, null); + code.StackMap.PushStack(CilType.SystemTypeType); + + code.NewInstruction(0xB8 /* invokestatic */, SpanType, + new JavaMethodRef("CompareTo" + CilMain.EXCLAMATION, + JavaType.IntegerType, SpanType, SpanType, CilType.SystemTypeType)); + + code.StackMap.PopStack(CilMain.Where); // null type + + return true; + } + return false; + } + + + internal static readonly CilType SpanType = CilType.From(new JavaType(0, 0, "system.Span$$1")); } diff --git a/CilToJava/src/GenericUtil.cs b/CilToJava/src/GenericUtil.cs index b6b664e..12908dd 100644 --- a/CilToJava/src/GenericUtil.cs +++ b/CilToJava/src/GenericUtil.cs @@ -322,6 +322,8 @@ namespace SpaceFlint.CilToJava if (! (fromType.IsInterface || fromType.IsDelegate)) return; + string varianceString = null; + bool anyVariance = false; foreach (var gp in defType.GenericParameters) { @@ -332,18 +334,31 @@ namespace SpaceFlint.CilToJava } } if (! anyVariance) - return; + { + 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 + return; + } // build a string that describes the generic variance - var chars = new char[defType.GenericParameters.Count]; - int idx = 0; - foreach (var gp in defType.GenericParameters) + if (varianceString == null) { - var v = gp.Attributes & GenericParameterAttributes.VarianceMask; - chars[idx++] = (v == GenericParameterAttributes.Covariant) ? 'O' - : (v == GenericParameterAttributes.Contravariant) ? 'I' - : ' '; + var chars = new char[defType.GenericParameters.Count]; + int idx = 0; + foreach (var gp in defType.GenericParameters) + { + var v = gp.Attributes & GenericParameterAttributes.VarianceMask; + chars[idx++] = (v == GenericParameterAttributes.Covariant) ? 'O' + : (v == GenericParameterAttributes.Contravariant) ? 'I' + : ' '; + } + varianceString = new string(chars); } var varianceField = new JavaField(); @@ -354,7 +369,7 @@ namespace SpaceFlint.CilToJava | JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_TRANSIENT | JavaAccessFlags.ACC_SYNTHETIC; - varianceField.Constant = new string(chars); + varianceField.Constant = varianceString; varianceField.Class = theClass; if (theClass.Fields == null) diff --git a/Tests/src/TestPointer.cs b/Tests/src/TestPointer.cs index 4119b63..a164114 100644 --- a/Tests/src/TestPointer.cs +++ b/Tests/src/TestPointer.cs @@ -11,7 +11,8 @@ namespace Tests { Test1(); Test2("Hello, world"); - //Test3(default(Guid)); + Test3(default(Guid)); + Test4(default(AAA)); } unsafe void Test1() @@ -59,12 +60,21 @@ namespace Tests Console.WriteLine(from + " -> " + new String(into) + "~~~"); } - /*unsafe void Test3(Guid from) + unsafe void Test3(Guid from) { Guid *pFrom = &from; *pFrom = new Guid("00000001-0000-0000-0000-000000000000"); Console.WriteLine(from); - }*/ + } + + struct AAA {} + + unsafe void Test4(AAA from) + { + AAA *pFrom = &from; + *pFrom = new AAA(); + Console.WriteLine(from); + } } } diff --git a/Tests/src/TestValue.cs b/Tests/src/TestValue.cs index 6ea0b76..01ef3c1 100644 --- a/Tests/src/TestValue.cs +++ b/Tests/src/TestValue.cs @@ -82,7 +82,7 @@ namespace Tests // Enum // - [Flags] enum MyEnum : sbyte { None, First = 1, Second = 8, Third = 32 }; + [Flags] enum MyEnum : short { None, First = 1, Second = 8, Third = 32 }; void TestEnum() { @@ -92,6 +92,14 @@ namespace Tests Console.Write("\t"); Console.Write(y); Console.Write($"\t{x:F},{x:G},{x:D},{x:X},{x.GetType().GetEnumUnderlyingType()}"); + Console.Write($"\t{((IConvertible) x).ToType(typeof(long), null)}"); + Console.Write($"\t{x.HasFlag(MyEnum.Third)}"); + Console.WriteLine(); + + var names = typeof(MyEnum).GetEnumNames(); + var values = typeof(MyEnum).GetEnumValues(); + for (int i = 0; i < names.Length; i++) + Console.Write($"\t{names[i]} = {(short) values.GetValue(i)}"); Console.WriteLine(); void TestEnum2(ref T e)