From 67ad9b0ccc18e17ef385e37fd4a4493beceea3da Mon Sep 17 00:00:00 2001 From: spaceflint <> Date: Sat, 13 Feb 2021 23:16:20 +0200 Subject: [PATCH] Some compat fixes for .NET 5 --- Baselib/mscorlib.filter | 3 + Baselib/src/System/Array.cs | 117 +++++++++++++++++++++----- Baselib/src/System/DateTime.cs | 2 + Baselib/src/System/GenericType.cs | 15 +++- Baselib/src/System/Int32.cs | 11 ++- Baselib/src/System/ParseNumbers.cs | 9 +- Baselib/src/System/Runtime/Marshal.cs | 21 +++++ Baselib/src/System/Span.cs | 10 ++- CilToJava/src/CilMain.cs | 1 + CilToJava/src/CodeCall.cs | 2 +- CilToJava/src/CodeCompare.cs | 6 +- CilToJava/src/CodeField.cs | 26 +++++- CilToJava/src/GenericUtil.cs | 5 ++ CilToJava/src/InterfaceBuilder.cs | 19 ++++- JavaBinary/src/JavaStackMap.cs | 2 +- Tests/src/TestCollection.cs | 15 +++- Tests/src/TestString.cs | 10 +++ 17 files changed, 235 insertions(+), 39 deletions(-) diff --git a/Baselib/mscorlib.filter b/Baselib/mscorlib.filter index 1bbc9f0..588c95f 100644 --- a/Baselib/mscorlib.filter +++ b/Baselib/mscorlib.filter @@ -57,6 +57,8 @@ System.IFormattable System.IFormatProvider System.InvalidCastException System.IServiceProvider +System.IObservable`* +System.IObserver`* System.Nullable`* @@ -188,6 +190,7 @@ System.Reflection.IReflect System.Reflection.MemberInfo System.Reflection.FieldInfo System.Reflection.EventInfo +System.Reflection.ParameterAttributes System.Reflection.ParameterInfo System.Reflection.PropertyInfo System.Reflection.MethodInfo diff --git a/Baselib/src/System/Array.cs b/Baselib/src/System/Array.cs index 3a11f01..cb88eb0 100644 --- a/Baselib/src/System/Array.cs +++ b/Baselib/src/System/Array.cs @@ -881,63 +881,56 @@ namespace system { ThrowIfNull(array); if (index < 0 || length < 0) - throw new System.ArgumentOutOfRangeException(); - if (array.len - index < length) - throw new System.ArgumentException(); + throw new System.IndexOutOfRangeException(); + var endIndex = index + length; + if (endIndex > array.len) + throw new System.IndexOutOfRangeException(); switch (array.arr) { case bool[] boolArray: - for (; length-- > 0; index++) - boolArray[index] = default(bool); + java.util.Arrays.fill(boolArray, index, endIndex, false); break; case sbyte[] byteArray: - for (; length-- > 0; index++) - byteArray[index] = default(sbyte); + java.util.Arrays.fill(byteArray, index, endIndex, (sbyte) 0); break; case char[] charArray: - for (; length-- > 0; index++) - charArray[index] = default(char); + java.util.Arrays.fill(charArray, index, endIndex, (char) 0); break; case short[] shortArray: - for (; length-- > 0; index++) - shortArray[index] = default(short); + java.util.Arrays.fill(shortArray, index, endIndex, (short) 0); break; case int[] intArray: - for (; length-- > 0; index++) - intArray[index] = default(int); + java.util.Arrays.fill(intArray, index, endIndex, (int) 0); break; case long[] longArray: - for (; length-- > 0; index++) - longArray[index] = default(long); + java.util.Arrays.fill(longArray, index, endIndex, (long) 0); break; case float[] floatArray: - for (; length-- > 0; index++) - floatArray[index] = default(float); + java.util.Arrays.fill(floatArray, index, endIndex, (float) 0); break; case double[] doubleArray: - for (; length-- > 0; index++) - doubleArray[index] = default(double); + java.util.Arrays.fill(doubleArray, index, endIndex, (double) 0); break; case object[] objectArray: if (system.RuntimeType.IsValueClass( - ((java.lang.Object) array.arr).getClass().getComponentType())) + ((java.lang.Object) (object) objectArray) + .getClass().getComponentType())) { for (; length-- > 0; index++) ((ValueMethod) (ValueType) objectArray[index]).Clear(); } else { - for (; length-- > 0; index++) - objectArray[index] = null; + java.util.Arrays.fill(objectArray, index, endIndex, null); } break; @@ -946,6 +939,86 @@ namespace system } } + // + // Fill + // + + public static void Fill(T[] array, T value) + { + ThrowIfNull(array); + FillCommon(array, (object) value, 0, java.lang.reflect.Array.getLength(array)); + } + + public static void Fill(T[] array, T value, int startIndex, int count) + { + ThrowIfNull(array); + if (startIndex < 0 || count < 0) + throw new System.IndexOutOfRangeException(); + var endIndex = startIndex + count; + if (endIndex > java.lang.reflect.Array.getLength(array)) + throw new System.IndexOutOfRangeException(); + FillCommon(array, (object) value, startIndex, endIndex); + } + + private static void FillCommon(object array, object value, int startIndex, int endIndex) + { + switch (array) + { + case bool[] boolArray: + java.util.Arrays.fill(boolArray, startIndex, endIndex, (bool) value); + break; + + case sbyte[] byteArray: + java.util.Arrays.fill(byteArray, startIndex, endIndex, (sbyte) value); + break; + + case char[] charArray: + java.util.Arrays.fill(charArray, startIndex, endIndex, (char) value); + break; + + case short[] shortArray: + java.util.Arrays.fill(shortArray, startIndex, endIndex, (short) value); + break; + + case int[] intArray: + java.util.Arrays.fill(intArray, startIndex, endIndex, (int) value); + break; + + case long[] longArray: + java.util.Arrays.fill(longArray, startIndex, endIndex, (long) value); + break; + + case float[] floatArray: + java.util.Arrays.fill(floatArray, startIndex, endIndex, (float) value); + break; + + case double[] doubleArray: + java.util.Arrays.fill(doubleArray, startIndex, endIndex, (double) value); + break; + + case object[] objectArray: + if (system.RuntimeType.IsValueClass( + ((java.lang.Object) array).getClass().getComponentType())) + { + ValueType v = (ValueType) value; + for (; startIndex < endIndex; startIndex++) + ((ValueMethod) v).CopyTo((ValueType) objectArray[startIndex]); + } + else + { + java.util.Arrays.fill(objectArray, startIndex, endIndex, value); + } + break; + + default: + throw new System.InvalidOperationException(); + } + } + + // + // Initialize + // + public void Initialize() { var type = ((java.lang.Object) arr).getClass().getComponentType(); diff --git a/Baselib/src/System/DateTime.cs b/Baselib/src/System/DateTime.cs index b0107b0..0de5601 100644 --- a/Baselib/src/System/DateTime.cs +++ b/Baselib/src/System/DateTime.cs @@ -25,6 +25,8 @@ namespace system // number of days between 1/1/0001 and 1/1/10000 is 3,652,059 const long MaxTicks = 3652059 * TicksPerDay - 1; + public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified); + // // Constructors (Now) // diff --git a/Baselib/src/System/GenericType.cs b/Baselib/src/System/GenericType.cs index b76bc80..b360ea5 100644 --- a/Baselib/src/System/GenericType.cs +++ b/Baselib/src/System/GenericType.cs @@ -33,7 +33,6 @@ namespace system { DelegateUtil.CopyBoxed(fromObj, toObj); } - } @@ -161,8 +160,22 @@ namespace system proxy = genericObject.TryCast(castToType); else proxy = Array.GetProxy(obj, castToType, true); + if (proxy == null) + { + if ( (obj != null) && (! castToType.IsGenericType) + && castToType is RuntimeType castToRuntimeType + && castToRuntimeType.JavaClassForArray() + .isAssignableFrom(((java.lang.Object) obj).getClass())) + { + // target type is not generic, but the object can be cast to + // it. this happens when both object and target type are one + // of the non-generic interfaces implemented by Array. + // see also IsArray() in GenericUtil::ShouldCallGenericCast. + return obj; + } ThrowInvalidCastException(obj, castToType); + } return proxy; } diff --git a/Baselib/src/System/Int32.cs b/Baselib/src/System/Int32.cs index 8c6eda1..10f8d33 100644 --- a/Baselib/src/System/Int32.cs +++ b/Baselib/src/System/Int32.cs @@ -141,14 +141,17 @@ namespace system try { // TODO: check if begins with NumberFormatInfo.Positive/NegativeSign - result = java.lang.Integer.parseInt(s.Trim()); - return true; + if (s != null) + { + result = java.lang.Integer.parseInt(s.Trim()); + return true; + } } catch (java.lang.NumberFormatException) { - result = 0; - return false; } + result = 0; + return false; } diff --git a/Baselib/src/System/ParseNumbers.cs b/Baselib/src/System/ParseNumbers.cs index 7d76b7c..554d786 100644 --- a/Baselib/src/System/ParseNumbers.cs +++ b/Baselib/src/System/ParseNumbers.cs @@ -35,6 +35,7 @@ namespace system int len = format.length(); + bool explicit_width = false; int width = -1; if (len == 1) width = 0; @@ -72,18 +73,22 @@ namespace system case 'D': c = isInt ? 'd' : (char) 0; + pfx = '0'; break; case 'N': if (isInt) { c = 'd'; - pfx = ','; sfx = "." + new string('0', width); width = 0; } else + { c = 'f'; + explicit_width = true; // set width even if zero + } + pfx = ','; break; case 'P': @@ -116,7 +121,7 @@ namespace system var format1 = "%"; if (pfx != 0) format1 += pfx; - if (width != 0) + if (explicit_width || width != 0) { if (! isInt) format1 += "."; diff --git a/Baselib/src/System/Runtime/Marshal.cs b/Baselib/src/System/Runtime/Marshal.cs index 2f0c797..baffe5f 100644 --- a/Baselib/src/System/Runtime/Marshal.cs +++ b/Baselib/src/System/Runtime/Marshal.cs @@ -5,6 +5,10 @@ namespace system.runtime.interopservices public static class Marshal { + [java.attr.RetainType] private static java.util.concurrent.ConcurrentHashMap MarshalTypes = + new java.util.concurrent.ConcurrentHashMap(); + + public static int SizeOf(System.Type t) { var j = ((system.RuntimeType) t).JavaClassForArray(); @@ -12,9 +16,26 @@ namespace system.runtime.interopservices : (j == java.lang.Character.TYPE || j == java.lang.Short.TYPE) ? 2 : (j == java.lang.Integer.TYPE || j == java.lang.Float.TYPE) ? 4 : (j == java.lang.Long.TYPE || j == java.lang.Double.TYPE) ? 8 + : MarshalTypes.get(t) is int sz ? sz : throw new System.PlatformNotSupportedException(); } + + public static bool SetComObjectData(object obj, object key, object data) + { + // this method is used to record extra types that Marshal.SizeOf + // should recognize, and the size returned for each such type + + #pragma warning disable 0252 + if (obj == typeof(Marshal) && key is System.Type && data is int) + { + MarshalTypes.put(key, data); + return true; + } + throw new System.PlatformNotSupportedException(); + //return false; + } + } } diff --git a/Baselib/src/System/Span.cs b/Baselib/src/System/Span.cs index 0e60186..f0a693c 100644 --- a/Baselib/src/System/Span.cs +++ b/Baselib/src/System/Span.cs @@ -145,7 +145,15 @@ namespace system [java.attr.RetainName] public static System.ValueType Assign(ValueType source) - => new Span((T[]) (object) (new ValueType[1] { source })); + { + return new Span() + { + array = Reference.Box(new ValueType[1] { source }), + count = 1, + shift = Shiftof() + 1 + }; + // => new Span((T[]) (object) (new ValueType[1] { source })); + } [java.attr.RetainName] public static Span Assign(long zero) diff --git a/CilToJava/src/CilMain.cs b/CilToJava/src/CilMain.cs index 3cbad9a..c7f5ab6 100644 --- a/CilToJava/src/CilMain.cs +++ b/CilToJava/src/CilMain.cs @@ -186,6 +186,7 @@ namespace SpaceFlint.CilToJava .Replace(']', '\uFF3D') // U+FF3D Fullwidth Right Square Bracket .Replace('`', '\uFF40') // U+FF40 Fullwidth Grave Accent .Replace('~', '\uFF5E') // U+FF40 Fullwidth Tilde + .Replace(' ', '\uFFE2') // U+FFE2 Fullwidth Not Sign ; } diff --git a/CilToJava/src/CodeCall.cs b/CilToJava/src/CodeCall.cs index 2acba77..a0ff6a2 100644 --- a/CilToJava/src/CodeCall.cs +++ b/CilToJava/src/CodeCall.cs @@ -811,7 +811,7 @@ namespace SpaceFlint.CilToJava var thisRef = stackMap.PopStack(CilMain.Where); if (updateThis && thisRef.Equals(JavaStackMap.UninitializedThis)) { - // an 'uninitializedthis' first argument is updated in + // an 'uninitializedThis' first argument is updated in // the stack frame after calling the super constructor locals.UpdateThis(method.DeclType); } diff --git a/CilToJava/src/CodeCompare.cs b/CilToJava/src/CodeCompare.cs index 08f6ab8..b0c5643 100644 --- a/CilToJava/src/CodeCompare.cs +++ b/CilToJava/src/CodeCompare.cs @@ -592,13 +592,15 @@ namespace SpaceFlint.CilToJava var castType = (CilType) CilType.From(cilType); JavaType castClass = CilType.From(cilType).AsWritableClass; - if (GenericUtil.ShouldCallGenericCast(stackTop, castType)) + if ( GenericUtil.ShouldCallGenericCast(stackTop, castType) + || castType.IsGenericParameter) { code.StackMap.PushStack(stackTop); // casting to a generic type is done via GenericType.TestCast GenericUtil.CastToGenericType(cilType, 0, code); code.StackMap.PopStack(CilMain.Where); // stackTop - code.NewInstruction(0xC0 /* checkcast */, castClass, null); + if (! castType.IsGenericParameter) + code.NewInstruction(0xC0 /* checkcast */, castClass, null); code.StackMap.PushStack(castClass); } diff --git a/CilToJava/src/CodeField.cs b/CilToJava/src/CodeField.cs index 8157912..bd01328 100644 --- a/CilToJava/src/CodeField.cs +++ b/CilToJava/src/CodeField.cs @@ -403,7 +403,31 @@ namespace SpaceFlint.CilToJava // is a value class that is assigned before the call to the // base constructor. - if (fldType.IsValueClass) + if (fldType.IsGenericParameter) + { + // if the field has a generic type, we first need to allocate + // it (as would be done by ValueUtil.ConstructValue) and then + // copy the value on the stack into it + + GenericUtil.LoadMaybeGeneric(fldType, code); + code.NewInstruction(0xB8 /* invokestatic */, GenericUtil.SystemGenericType, + new JavaMethodRef("New", CilType.SystemValueType, CilType.SystemTypeType)); + + // use dup_x1 to arrange the stack as (newObj, sourceObj, newObj) + code.NewInstruction(0x5A /* dup_x1 */, null, null); + stackMap.PushStack(fldType); + stackMap.PushStack(fldType); + + // call GenericType.Copy(fromObj, toObj) in baselib + code.NewInstruction(0xB8 /* invokestatic */, GenericUtil.SystemGenericType, + new JavaMethod("Copy", JavaType.VoidType, + JavaType.ObjectType, JavaType.ObjectType)); + + stackMap.PopStack(CilMain.Where); + stackMap.PopStack(CilMain.Where); + } + + else if (fldType.IsValueClass) { CilMethod.ValueMethod(CilMethod.ValueClone, code); code.NewInstruction(0xC0 /* checkcast */, fldType.AsWritableClass, null); diff --git a/CilToJava/src/GenericUtil.cs b/CilToJava/src/GenericUtil.cs index 53e2c3e..53416b1 100644 --- a/CilToJava/src/GenericUtil.cs +++ b/CilToJava/src/GenericUtil.cs @@ -136,6 +136,11 @@ namespace SpaceFlint.CilToJava if (code.MaxStack < 1) code.MaxStack = 1; + // we are injecting a call to super constructor at the very top, + // so local 0 should have the proper type, not uninitializedThis + code.StackMap.SetLocalInAllFrames( + 0, CilType.From(new JavaType(0, 0, dataClass.Name)), null); + insertReturn = false; } diff --git a/CilToJava/src/InterfaceBuilder.cs b/CilToJava/src/InterfaceBuilder.cs index 4c92367..0544332 100644 --- a/CilToJava/src/InterfaceBuilder.cs +++ b/CilToJava/src/InterfaceBuilder.cs @@ -600,9 +600,22 @@ namespace SpaceFlint.CilToJava } if (sameParameters) { - //Console.WriteLine($"proxying {targetMethod} in class {targetMethod.DeclType}"); - BuildGenericProxy2(baseMethod, targetMethod, - false, targetMethod.DeclType, intoClass); + if (baseMethod.Method.WithGenericParameters.ToString() + != targetMethod.WithGenericParameters.ToString()) + { + // the proxy method may have the same signature as the + // target method. for example in a generic class that + // inherits from a generic class: + // class A { virtual T Method(T arg); } + // and class B : A { override T Method(T arg); } + // in such a case, we should not generate a specialized + // proxy for the generic method in class B. + // + + //Console.WriteLine($"proxying {targetMethod} in class {targetMethod.DeclType}"); + BuildGenericProxy2(baseMethod, targetMethod, + false, targetMethod.DeclType, intoClass); + } return; } } diff --git a/JavaBinary/src/JavaStackMap.cs b/JavaBinary/src/JavaStackMap.cs index a861aa8..a02e0de 100644 --- a/JavaBinary/src/JavaStackMap.cs +++ b/JavaBinary/src/JavaStackMap.cs @@ -358,7 +358,7 @@ namespace SpaceFlint.JavaBinary { if (! frm.locals[index].Equals(type)) { - if (frm.locals[index] != Top) + if (Where != null && frm.locals[index] != Top) throw Where.Exception($"local already assigned in stack frame"); frm.locals[index] = type; } diff --git a/Tests/src/TestCollection.cs b/Tests/src/TestCollection.cs index b1ddcd3..088ef94 100644 --- a/Tests/src/TestCollection.cs +++ b/Tests/src/TestCollection.cs @@ -23,8 +23,8 @@ namespace Tests //TestDictionary(); TestSet(); - TestStack(); + TestHash(); } @@ -127,5 +127,18 @@ namespace Tests } } + + + public void TestHash() + { + var dict = new System.Collections.Hashtable(); + dict["int1"] = (int) 10; + dict["int2"] = (int) 20; + int sum = 0; + foreach (var key in dict.Keys) + sum += (int) dict[key]; + Console.WriteLine(sum); + } + } } diff --git a/Tests/src/TestString.cs b/Tests/src/TestString.cs index 676f66f..3ce122e 100644 --- a/Tests/src/TestString.cs +++ b/Tests/src/TestString.cs @@ -16,6 +16,7 @@ namespace Tests TestCompare(); TestCast("Test"); TestEncoding(); + TestNumber(); } @@ -244,5 +245,14 @@ namespace Tests Console.WriteLine(string.CompareOrdinal(aString, bString)); } + + + void TestNumber() + { + int x = 1; + var y = 33333.030303; + Console.WriteLine($"[{x:D3}] [{y:N0}]"); + } + } }