diff --git a/CLI/AssemblyBuilder.cs b/CLI/AssemblyBuilder.cs index c474179..fd92f5d 100755 --- a/CLI/AssemblyBuilder.cs +++ b/CLI/AssemblyBuilder.cs @@ -5,6 +5,7 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using JCIL.CLI.Types; using JCIL.CLI.Types.java.lang; +using JCIL.Core; using JCIL.Java.Class; using Array = System.Array; using OpCode = JCIL.Java.Class.OpCode; @@ -14,13 +15,10 @@ namespace JCIL.CLI; public sealed class AssemblyBuilder { public readonly System.Reflection.Emit.AssemblyBuilder Assembly; - private readonly ModuleBuilder _module; - private readonly ConcurrentDictionary _classes = []; - private readonly ConcurrentDictionary _fields = []; - private readonly ConcurrentDictionary _methods = []; + public readonly ModuleBuilder Module; + private readonly ConcurrentDictionary _classes = []; public int CompiledMethods { get; private set; } - public int TotalMethods => _methods.Count; public AssemblyBuilder(AssemblyName name, bool runnable) { @@ -28,10 +26,10 @@ public sealed class AssemblyBuilder ? System.Reflection.Emit.AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect) : new PersistedAssemblyBuilder(name, typeof(int).Assembly); - _module = Assembly.DefineDynamicModule("JCIL_Output"); + Module = Assembly.DefineDynamicModule("JCIL_Output"); } - public TypeSurrogate MakeType(Class javaClass) + public TypeRepresentation MakeType(Class javaClass) { if (_classes.TryGetValue(javaClass, out var type)) return type; @@ -43,33 +41,39 @@ public sealed class AssemblyBuilder switch (intrinsic.Type) { case IntrinsicType.Byte: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Byte(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Byte(this, c)); case IntrinsicType.Char: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Char(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Char(this, c)); case IntrinsicType.Int: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Integer(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Integer(this, c)); case IntrinsicType.Short: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Short(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Short(this, c)); case IntrinsicType.Long: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Long(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Long(this, c)); case IntrinsicType.Boolean: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Boolean(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Boolean(this, c)); case IntrinsicType.Float: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Float(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Float(this, c)); case IntrinsicType.Double: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Double(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Double(this, c)); case IntrinsicType.Void: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Void(c)); + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Void(this, c)); default: throw new NotImplementedException($"Unimplemented Intrinsic {intrinsic.Type}"); } } case ArrayMetadata array: - return _classes.GetOrAdd(javaClass, c => new ArrayOf(MakeType(array.ElementClass), c)); + return _classes.GetOrAdd(javaClass, c => new ArrayOf(this, MakeType(array.ElementClass), c)); + } + + if (javaClass.Namespace.Span is "java/lang") + { + if(javaClass.Name.Span is "String") + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.String(this, c)); + + if(javaClass.Name.Span is "Object") + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Object(this, c)); } - if(javaClass is {Namespace.Span: "java/lang", Name.Span: "String" }) - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.String(c)); - var newClass = (NewClass)_classes.GetOrAdd(javaClass, _ => { TypeAttributes flags = default; @@ -80,17 +84,22 @@ public sealed class AssemblyBuilder var name = javaClass.Namespace.IsEmpty ? javaClass.Name.ToString() : $"{javaClass.Namespace.ToString().Replace('/', '.')}.{javaClass.Name}"; - var builder = _module.DefineType(name, flags); + + var builder = Module.DefineType(name, flags); + return new NewClass(this, builder, javaClass); }); if (javaClass.SuperClass is {} super) - newClass.Type.SetParent(MakeType(super).Type); + newClass.TypeBuilder.SetParent(MakeType(super).Type); + + foreach (var javaInterface in javaClass.Interfaces) + newClass.TypeBuilder.AddInterfaceImplementation(MakeType(javaInterface).Type); newClass.CreateFields(); return newClass; } - + internal void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, OpCodeReader opcodes) { @@ -145,42 +154,10 @@ public sealed class AssemblyBuilder return labels[position + (short)opcode.P0.UShort]; } - void AssertCast(Value value, Type type) - { - if(value.Type == type) return; - - if (type == typeof(int)) - { - if (value.Type == typeof(byte) || value.Type == typeof(short) || value.Type == typeof(char) || value.Type == typeof(bool)) - { - il.Emit(OpCodes.Conv_I4); - return; - } - } - - if (type == typeof(byte)) - { - if (value.Type == typeof(int) || value.Type == typeof(short) || value.Type == typeof(char)) - { - il.Emit(OpCodes.Conv_I1); - return; - } - } - - if (value.Type.IsAssignableTo(type)) - return; - - if(type == typeof(object) && value.Type.IsArray) - return; - - - throw new InvalidOperationException($"Value of type `{value.Type}` cannot be assigned to one of type `{type}`."); - } - switch (opcode.Mnemonic) { case OpCodeMnemonic.AConstNull: - stack.Push(new Value(typeof(object))); + stack.Push(Value.Null); il.Emit(OpCodes.Ldnull); break; @@ -189,30 +166,36 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.IConst2: case OpCodeMnemonic.IConst3: case OpCodeMnemonic.IConst4: - case OpCodeMnemonic.IConst5: - stack.Push(new Value(opcode.Mnemonic - OpCodeMnemonic.IConst0)); + case OpCodeMnemonic.IConst5: + stack.Push(new Value(opcode.Mnemonic - OpCodeMnemonic.IConst0).Load(il)); break; + + case OpCodeMnemonic.IConstM1: stack.Push(new Value(-1).Load(il)); break; + + case OpCodeMnemonic.LConst0: stack.Push(new Value(0L).Load(il)); break; + case OpCodeMnemonic.LConst1: stack.Push(new Value(1L).Load(il)); break; + + case OpCodeMnemonic.FConst0: stack.Push(new Value(0f).Load(il)); break; + case OpCodeMnemonic.FConst1: stack.Push(new Value(1f).Load(il)); break; + case OpCodeMnemonic.FConst2: stack.Push(new Value(2f).Load(il)); break; - case OpCodeMnemonic.LConst0: - stack.Push(new Value(typeof(long))); - il.Emit(OpCodes.Ldc_I8, 0L); - break; - - case OpCodeMnemonic.LConst1: - stack.Push(new Value(typeof(long))); - il.Emit(OpCodes.Ldc_I8, 1L); - break; + case OpCodeMnemonic.DConst0: stack.Push(new Value(0d).Load(il)); break; + case OpCodeMnemonic.DConst1: stack.Push(new Value(1d).Load(il)); break; case OpCodeMnemonic.PushByte: stack.Push(new Value(opcode.P0.Byte).Load(il)); break; case OpCodeMnemonic.PushShort: stack.Push(new Value((short)opcode.P0.UShort).Load(il)); break; case OpCodeMnemonic.Ldc: case OpCodeMnemonic.LdcW: + case OpCodeMnemonic.Ldc2W: { var constant = javaClass.GetConstant(opcode.P0.UShort); switch (constant) { case int val: stack.Push(new Value(val).Load(il)); break; + case long val: stack.Push(new Value(val).Load(il)); break; + case float val: stack.Push(new Value(val).Load(il)); break; + case double val: stack.Push(new Value(val).Load(il)); break; case string val: stack.Push(new Value(val).Load(il)); break; case Class val: stack.Push(Value.TypeRef(MakeType(val).Type).Load(il)); break; default: @@ -242,7 +225,7 @@ public sealed class AssemblyBuilder }; ref var loc = ref locals[idx]; - AssertCast(loc, typeof(int)); + AssertCast(loc, typeof(int), il); stack.Push(loc.Load(il)); break; } @@ -264,7 +247,30 @@ public sealed class AssemblyBuilder }; ref var loc = ref locals[idx]; - AssertCast(loc, typeof(long)); + AssertCast(loc, typeof(long), il); + stack.Push(loc.Load(il)); + break; + } + + + case OpCodeMnemonic.FLoad: + case OpCodeMnemonic.FLoad0: + case OpCodeMnemonic.FLoad1: + case OpCodeMnemonic.FLoad2: + case OpCodeMnemonic.FLoad3: + { + var idx = opcode.Mnemonic switch + { + OpCodeMnemonic.FLoad0 => 0, + OpCodeMnemonic.FLoad1 => 1, + OpCodeMnemonic.FLoad2 => 2, + OpCodeMnemonic.FLoad3 => 3, + OpCodeMnemonic.FLoad => opcode.P0.UShort, + _ => -1, + }; + + ref var loc = ref locals[idx]; + AssertCast(loc, typeof(float), il); stack.Push(loc.Load(il)); break; } @@ -292,6 +298,8 @@ public sealed class AssemblyBuilder break; } + case OpCodeMnemonic.IALoad: + case OpCodeMnemonic.BALoad: case OpCodeMnemonic.AALoad: { var idx = stack.Pop(); @@ -299,11 +307,28 @@ public sealed class AssemblyBuilder AssertIntCompatible(idx); AssertArray(array.Type); + var elementType = array.Type.GetElementType()!; - if (array.Type.GetElementType() is not { IsClass: true } elementType) - throw new InvalidOperationException("Array element is not an object."); - - il.Emit(OpCodes.Ldelem_Ref, elementType); + switch (opcode.Mnemonic) + { + case OpCodeMnemonic.IALoad: + if (elementType != typeof(int) && elementType != typeof(int)) + throw new InvalidOperationException($"Expected int, found `{elementType}`."); + il.Emit(OpCodes.Ldelem_I4); + break; + + case OpCodeMnemonic.BALoad: + if (elementType != typeof(byte) && elementType != typeof(bool)) + throw new InvalidOperationException($"Expected byte or boolean, found `{elementType}`."); + il.Emit(OpCodes.Ldelem_I1); + break; + + case OpCodeMnemonic.AALoad: + if (!elementType.IsClass) + throw new InvalidOperationException($"Expected object, found `{elementType}`."); + il.Emit(OpCodes.Ldelem_Ref, elementType); + break; + } stack.Push(new Value(elementType)); break; } @@ -315,7 +340,7 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.IStore3: { var value = stack.Pop(); - AssertCast(value, typeof(int)); + AssertCast(value, typeof(int), il); var idx = opcode.Mnemonic switch { OpCodeMnemonic.IStore => opcode.P0.UShort, @@ -338,7 +363,7 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.LStore3: { var value = stack.Pop(); - AssertCast(value, typeof(long)); + AssertCast(value, typeof(long), il); var idx = opcode.Mnemonic switch { OpCodeMnemonic.LStore => opcode.P0.UShort, @@ -361,7 +386,7 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.AStore3: { var value = stack.Pop(); - AssertCast(value, typeof(object)); + AssertCast(value, typeof(object), il); var idx = opcode.Mnemonic switch { OpCodeMnemonic.AStore => opcode.P0.UShort, @@ -377,6 +402,8 @@ public sealed class AssemblyBuilder break; } + case OpCodeMnemonic.IAStore: + case OpCodeMnemonic.BAStore: case OpCodeMnemonic.AAStore: { var value = stack.Pop(); @@ -386,8 +413,15 @@ public sealed class AssemblyBuilder AssertIntCompatible(idx); var eType = array.Type.GetElementType()!; - AssertCast(value, eType); - il.Emit(OpCodes.Stelem_Ref); + AssertCast(value, eType, il); + + il.Emit(opcode.Mnemonic switch + { + OpCodeMnemonic.BAStore => OpCodes.Stelem_I1, + OpCodeMnemonic.IAStore => OpCodes.Stelem_I4, + OpCodeMnemonic.AAStore => OpCodes.Stelem_Ref, + _ => throw new UnreachableException(), + }); break; } @@ -414,8 +448,8 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.IRem: case OpCodeMnemonic.IOr: case OpCodeMnemonic.IAnd: - case OpCodeMnemonic.IShr: case OpCodeMnemonic.IShl: + case OpCodeMnemonic.IShr: case OpCodeMnemonic.IUShr: { var a = stack.Pop(); @@ -431,8 +465,8 @@ public sealed class AssemblyBuilder OpCodeMnemonic.IRem => OpCodes.Rem, OpCodeMnemonic.IOr => OpCodes.Or, OpCodeMnemonic.IAnd => OpCodes.And, - OpCodeMnemonic.IShr => OpCodes.Shr, OpCodeMnemonic.IShl => OpCodes.Shl, + OpCodeMnemonic.IShr => OpCodes.Shr, OpCodeMnemonic.IUShr => OpCodes.Shr_Un, _ => throw new UnreachableException(), }); @@ -447,6 +481,9 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.LRem: case OpCodeMnemonic.LOr: case OpCodeMnemonic.LAnd: + case OpCodeMnemonic.LShl: + case OpCodeMnemonic.LShr: + case OpCodeMnemonic.LUShr: { var a = stack.Pop(); var b = stack.Pop(); @@ -461,12 +498,56 @@ public sealed class AssemblyBuilder OpCodeMnemonic.LRem => OpCodes.Rem, OpCodeMnemonic.LOr => OpCodes.Or, OpCodeMnemonic.LAnd => OpCodes.And, + OpCodeMnemonic.LShl => OpCodes.Shl, + OpCodeMnemonic.LShr => OpCodes.Shr, + OpCodeMnemonic.LUShr => OpCodes.Shr_Un, _ => throw new UnreachableException(), }); stack.Push(new Value(typeof(long))); break; } + + case OpCodeMnemonic.IntToLong: + { + var value = stack.Pop(); + AssertIntCompatible(value.Type); + il.Emit(OpCodes.Conv_I8); + stack.Push(new Value(typeof(long))); + break; + } + + case OpCodeMnemonic.LongToInt: + { + var value = stack.Pop(); + AssertLongCompatible(value.Type); + il.Emit(OpCodes.Conv_I4); + stack.Push(new Value(typeof(int))); + break; + } + + case OpCodeMnemonic.FCmpL: + { + var a = stack.Pop(); + var b = stack.Pop(); + var m = ILHelpers.FCmpL; + if (a.Type != typeof(float)) throw new InvalidOperationException($"Expected float, found `{a.Type}`"); + if (b.Type != typeof(float)) throw new InvalidOperationException($"Expected float, found `{b.Type}`"); + il.Emit(OpCodes.Call, m.Method); + stack.Push(new Value(typeof(int))); + break; + } + case OpCodeMnemonic.FCmpG: + { + var a = stack.Pop(); + var b = stack.Pop(); + var m = ILHelpers.FCmpG; + if (a.Type != typeof(float)) throw new InvalidOperationException($"Expected float, found `{a.Type}`"); + if (b.Type != typeof(float)) throw new InvalidOperationException($"Expected float, found `{b.Type}`"); + il.Emit(OpCodes.Call, m.Method); + stack.Push(new Value(typeof(int))); + break; + } case OpCodeMnemonic.IfEq: { @@ -505,6 +586,36 @@ public sealed class AssemblyBuilder il.Emit(OpCodes.Brfalse, JmpLabel()); break; } + + case OpCodeMnemonic.IfICmpNe: + { + var a = stack.Pop(); + var b = stack.Pop(); + AssertIntCompatible(a); + AssertIntCompatible(b); + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Brfalse, JmpLabel()); + break; + } + + case OpCodeMnemonic.IfICmpEq: + case OpCodeMnemonic.IfICmpLt: + case OpCodeMnemonic.IfICmpGt: + { + var a = stack.Pop(); + var b = stack.Pop(); + AssertIntCompatible(a); + AssertIntCompatible(b); + il.Emit(opcode.Mnemonic switch + { + OpCodeMnemonic.IfICmpEq => OpCodes.Ceq, + OpCodeMnemonic.IfICmpLt => OpCodes.Clt, + OpCodeMnemonic.IfICmpGt => OpCodes.Cgt, + _ => throw new UnreachableException(), + }); + il.Emit(OpCodes.Brtrue, JmpLabel()); + break; + } case OpCodeMnemonic.IfACmpNe: { @@ -543,8 +654,7 @@ public sealed class AssemblyBuilder { var fieldRef = javaClass.GetConstant(opcode.P0.UShort); var parent = MakeType(fieldRef.Class); - var field = parent.GetField(fieldRef.Field.Name); - stack.Push(new Value(field).Load(il)); + stack.Push(parent.GetField(fieldRef.Field.Name, il)); break; } @@ -552,13 +662,12 @@ public sealed class AssemblyBuilder { var fieldRef = javaClass.GetConstant(opcode.P0.UShort); var parent = MakeType(fieldRef.Class); - var field = parent.GetField(fieldRef.Field.Name); var value = stack.Pop(); if (!value.Type.IsAssignableTo(parent.Type)) - throw new InvalidOperationException($"Value is not compatible with type {parent}."); + throw new InvalidOperationException($"Value of type `{value.Type.FullName}` is not compatible with type `{parent.Type.FullName}`."); - stack.Push(new Value(field).Load(il)); + stack.Push(parent.GetField(fieldRef.Field.Name, il)); break; } @@ -566,17 +675,15 @@ public sealed class AssemblyBuilder { var fieldRef = javaClass.GetConstant(opcode.P0.UShort); var parent = MakeType(fieldRef.Class); - var field = parent.GetField(fieldRef.Field.Name); var value = stack.Pop(); - - AssertCast(value, field.FieldType); - il.Emit(OpCodes.Stfld, field); + parent.SetField(fieldRef.Field.Name, value, il); break; } case OpCodeMnemonic.InvokeVirtual: case OpCodeMnemonic.InvokeSpecial: case OpCodeMnemonic.InvokeStatic: + case OpCodeMnemonic.InvokeInterface: { var methodRef = javaClass.GetConstant(opcode.P0.UShort); var parent = MakeType(methodRef.Class); @@ -584,44 +691,53 @@ public sealed class AssemblyBuilder var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory()) .SpecialClassMetadata!; - for (var i = 0; i < signature.ParamTypes.Count; i++) stack.Pop(); - stack.Push(new Value(parent.CallMethod(methodRef.Method.Name, signature, il))); + parent.CallMethod(methodRef.Method.Name, signature, stack, il); break; } case OpCodeMnemonic.New: { var type = MakeType(javaClass.GetConstant(opcode.P0.UShort)); - var posBackup = opcodes.Reader.Stream.Position; - var stackBackup = new Stack(stack); - opcodes.MoveNext(); - if (opcodes.Current.Mnemonic == OpCodeMnemonic.Dup) + if (type is not TypeSurrogate) { + var posBackup = opcodes.Reader.Stream.Position; + var stackBackup = new Stack(stack); opcodes.MoveNext(); - position = opcodes.Reader.Stream.Position; - while (opcodes.Current.Mnemonic != OpCodeMnemonic.InvokeSpecial) + if (opcodes.Current.Mnemonic == OpCodeMnemonic.Dup) { - CompileOpCode(position, opcodes.Current); opcodes.MoveNext(); position = opcodes.Reader.Stream.Position; + while (opcodes.Current.Mnemonic != OpCodeMnemonic.InvokeSpecial) + { + CompileOpCode(position, opcodes.Current); + opcodes.MoveNext(); + position = opcodes.Reader.Stream.Position; + } + + var methodRef = javaClass.GetConstant(opcodes.Current.P0.UShort); + var parent = MakeType(methodRef.Class); + + var signature = (MethodSignature)javaClass.Loader + .GetClass(methodRef.Method.Descriptor.AsMemory()) + .SpecialClassMetadata!; + + if (parent is NewClass newClass) + { + var ctor = (ConstructorInfo)newClass.GetMethod(methodRef.Method.Name, signature); + while (stack.Count > stackBackup.Count) stack.Pop(); + il.Emit(OpCodes.Newobj, ctor); + stack.Push(new Value(parent.Type)); + } + else + { + throw new NotImplementedException(); + } + break; } - - var methodRef = javaClass.GetConstant(opcodes.Current.P0.UShort); - var parent = MakeType(methodRef.Class); - - var signature = (MethodSignature)javaClass.Loader - .GetClass(methodRef.Method.Descriptor.AsMemory()) - .SpecialClassMetadata!; - - var ctor = (ConstructorInfo)parent.GetMethod(methodRef.Method.Name, signature); - while (stack.Count > stackBackup.Count) stack.Pop(); - il.Emit(OpCodes.Newobj, ctor); - stack.Push(new Value(parent.Type)); - break; + opcodes.Reader.Stream.Position = posBackup; + stack = stackBackup; } - - opcodes.Reader.Stream.Position = posBackup; - stack = stackBackup; + var func = RuntimeHelpers.GetUninitializedObject; il.Emit(OpCodes.Ldtoken, type.Type); il.Emit(OpCodes.Call, func.Method); @@ -667,6 +783,29 @@ public sealed class AssemblyBuilder break; } + case OpCodeMnemonic.CheckCast: + { + var type = MakeType(javaClass.GetConstant(opcode.P0.UShort)); + var value = stack.Pop(); + AssertClass(value.Type); + il.Emit(OpCodes.Castclass, type.Type); + stack.Push(new Value(type.Type)); + break; + } + + case OpCodeMnemonic.InstanceOf: + { + var type = MakeType(javaClass.GetConstant(opcode.P0.UShort)); + var value = stack.Pop(); + AssertClass(value.Type); + il.Emit(OpCodes.Isinst, type.Type); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Castclass, type.Type); + il.Emit(OpCodes.Ceq); + stack.Push(new Value(typeof(int))); + break; + } + case OpCodeMnemonic.IfNull: { AssertClass(stack.Pop().Type); @@ -719,9 +858,59 @@ public sealed class AssemblyBuilder return labels; } + internal static void AssertCast(Value value, Type type, ILGenerator il) + { + if(ReferenceEquals(type, value.Type)) + return; + + if (type == typeof(int)) + { + if (value.Type == typeof(byte) || value.Type == typeof(short) || value.Type == typeof(char) || value.Type == typeof(bool)) + { + il.Emit(OpCodes.Conv_I4); + return; + } + } + + if (type == typeof(long)) + { + if (value.Type == typeof(byte) || value.Type == typeof(short) || value.Type == typeof(int) || value.Type == typeof(char) || value.Type == typeof(bool)) + { + il.Emit(OpCodes.Conv_I8); + return; + } + } + + if (type == typeof(byte) || type == typeof(bool)) + { + if (value.Type == typeof(int) || value.Type == typeof(short) || value.Type == typeof(char)) + { + il.Emit(OpCodes.Conv_I1); + return; + } + } + + if (value.Type.IsArray) + { + if(type == typeof(object)) + return; + + if(type.IsArray && type.GetElementType() == value.Type.GetElementType()) + return; + } + + if (value.IsNull && !type.IsValueType) + return; + + if(IsAssignableTo(value.Type, type)) + return; + + throw new InvalidOperationException($"Value of type `{value.Type}` cannot be assigned to one of type `{type}`."); + } + private static void AssertClass(Type type) { - if (type is { IsClass: false, IsInterface: false }) + if (type.IsValueType) throw new InvalidOperationException($"Value of type `{type}` is not an object."); } @@ -751,24 +940,18 @@ public sealed class AssemblyBuilder if(type == typeof(bool)) return; throw new InvalidOperationException($"Value of type `{type}` is not an integer."); } -} -internal class LazyMethod(MethodBase method, Action compile) -{ - public bool Compiled { get; private set; } - - public MethodBase Method + private static bool IsAssignableTo(Type from, Type to) { - get - { - if (!Compiled) - { - Compiled = true; - compile(); - } + if (ReferenceEquals(from, to)) return true; + if(from.BaseType is {} baseType && IsAssignableTo(baseType, to)) + return true; + + foreach (var type in from.GetInterfaces()) + if(IsAssignableTo(type, to)) + return true; - return method; - } + return false; } } @@ -782,6 +965,7 @@ internal readonly struct Value private LoadDelegate? _load { get; init; } private StoreDelegate? _store { get; init; } private OpCodeParameter _data { get; init; } + public bool IsNull => Type == typeof(object) && _data.Byte == 1; public static implicit operator Type(Value v) => v.Type; public Value Load(ILGenerator il) @@ -826,6 +1010,39 @@ internal readonly struct Value return new Value(val); }; } + + public Value(long value) + { + Type = typeof(long); + _data = new OpCodeParameter { ULong = (ulong) value }; + _load = (il, data) => + { + il.Emit(OpCodes.Ldc_I8, (long) data.ULong); + return new Value((long)data.ULong); + }; + } + + public Value(float value) + { + Type = typeof(float); + _data = new OpCodeParameter { Float = value }; + _load = (il, data) => + { + il.Emit(OpCodes.Ldc_R4, data.Float); + return new Value(data.Float); + }; + } + + public Value(double value) + { + Type = typeof(double); + _data = new OpCodeParameter { Double = value }; + _load = (il, data) => + { + il.Emit(OpCodes.Ldc_R8, data.Double); + return new Value(data.Double); + }; + } public Value(string value) { @@ -850,6 +1067,8 @@ internal readonly struct Value return new Value(val); }; } + + public static readonly Value Null = new(typeof(object)) { _data = new OpCodeParameter { Byte = 1 }}; public static Value TypeRef(Type type) { diff --git a/CLI/CLI.csproj b/CLI/CLI.csproj index 3c45117..b880dab 100755 --- a/CLI/CLI.csproj +++ b/CLI/CLI.csproj @@ -7,6 +7,7 @@ enable JCIL.CLI JCIL.CLI + true diff --git a/CLI/Program.cs b/CLI/Program.cs index 3c2a2d2..1cf4702 100755 --- a/CLI/Program.cs +++ b/CLI/Program.cs @@ -24,14 +24,14 @@ public class Program } catch (InvalidDataException e) { - Console.WriteLine($"Compiled methods: {builder.CompiledMethods}/{builder.TotalMethods}"); + Console.WriteLine($"Compiled methods: {builder.CompiledMethods}"); Console.ForegroundColor = ConsoleColor.Red; Console.Error.WriteLine(e.Message); Environment.Exit(1); } finally { - Console.WriteLine($"Compiled methods: {builder.CompiledMethods}/{builder.TotalMethods}"); + Console.WriteLine($"Compiled methods: {builder.CompiledMethods}"); var assembly = (PersistedAssemblyBuilder)builder.Assembly; assembly.Save("out.dll"); } @@ -40,5 +40,5 @@ public class Program internal static class Debug { - public static readonly IndentedTextWriter Writer = new(Console.Error, "| "); + public static readonly IndentedTextWriter Writer = new(Console.Error, "| "); } diff --git a/CLI/Types/New.cs b/CLI/Types/New.cs index e11c080..59ef612 100644 --- a/CLI/Types/New.cs +++ b/CLI/Types/New.cs @@ -6,32 +6,47 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types; -public sealed class NewClass : TypeSurrogate +public class NewClass : TypeRepresentation { public readonly Class Original; - public override TypeBuilder Type { get; } + public override Type Type => TypeBuilder; - private readonly AssemblyBuilder _builder; + public readonly TypeBuilder TypeBuilder; + protected readonly AssemblyBuilder Builder; private FrozenDictionary _fields = FrozenDictionary.Empty; - private readonly ConcurrentDictionary<(string, MethodSignature), MethodBase> _methods = []; + protected readonly ConcurrentDictionary<(string, MethodSignature), MethodBase> Methods = []; public NewClass(AssemblyBuilder builder, TypeBuilder type, Class original) { - Type = type; - _builder = builder; + TypeBuilder = type; + Builder = builder; Original = original; } - public override FieldInfo GetField(string name) + internal override Value GetField(string name, ILGenerator il) { - return _fields[name]; + var field = _fields[name]; + il.Emit(field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, field); + return new Value(field.FieldType); } - public override MethodBase GetMethod(string name, MethodSignature signature) + internal override void SetField(string name, Value value, ILGenerator il) + { + var field = _fields[name]; + AssemblyBuilder.AssertCast(value, field.FieldType, il); + il.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field); + } + + public override MethodBase GetMethod(Method method) + { + return GetMethod(method.Name, (MethodSignature) method.Type.SpecialClassMetadata!); + } + + public virtual MethodBase GetMethod(string name, MethodSignature signature) { Method? original = null; - var method = _methods.GetOrAdd((name, signature), _ => + var method = Methods.GetOrAdd((name, signature), _ => { original = Original.GetMethod(name, signature); if (original == null) throw new KeyNotFoundException($"Could not find method `{name}`"); @@ -46,16 +61,16 @@ public sealed class NewClass : TypeSurrogate signature = (MethodSignature) original.Type.SpecialClassMetadata!; if (original.Name == "") { - return Type.DefineConstructor( + return TypeBuilder.DefineConstructor( attributes, CallingConventions.Standard, - signature.ParamTypes.Select(t => _builder.MakeType(t).Type).ToArray() + signature.ParamTypes.Select(t => Builder.MakeType(t).Type).ToArray() ); } - - return Type.DefineMethod( + + return TypeBuilder.DefineMethod( name, attributes, CallingConventions.Standard, - _builder.MakeType(signature.ReturnType).Type, - signature.ParamTypes.Select(t => _builder.MakeType(t).Type).ToArray() + Builder.MakeType(signature.ReturnType).Type, + signature.ParamTypes.Select(t => Builder.MakeType(t).Type).ToArray() ); }); @@ -66,33 +81,45 @@ public sealed class NewClass : TypeSurrogate return method; if (method is MethodBuilder methodBuilder) - _builder.CompileOpCodes(Original, method, methodBuilder.GetILGenerator(), code, reader); + Builder.CompileOpCodes(original.DeclaringClass, method, methodBuilder.GetILGenerator(), code, reader); if (method is ConstructorBuilder constructorBuilder) - _builder.CompileOpCodes(Original, method, constructorBuilder.GetILGenerator(), code, reader); + Builder.CompileOpCodes(original.DeclaringClass, method, constructorBuilder.GetILGenerator(), code, reader); return method; } - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) + internal override Type CallMethod(string name, MethodSignature signature, Stack stack, ILGenerator il) { var method = GetMethod(name, signature); + if (!method.IsStatic) stack.Pop(); + for (var i = 0; i < signature.ParamTypes.Count; i++) stack.Pop(); + + Type type; switch (method) { case MethodInfo info when method.IsVirtual: il.Emit(OpCodes.Callvirt, info); - return info.ReturnType; + type = info.ReturnType; + break; case MethodInfo info: il.Emit(OpCodes.Call, info); - return info.ReturnType; + type = info.ReturnType; + break; case ConstructorInfo info: il.Emit(OpCodes.Call, info); - return typeof(void); + type = typeof(void); + break; default: throw new NotImplementedException(); } + + if(type != typeof(void)) + stack.Push(new Value(type)); + + return type; } internal void CreateFields() @@ -106,11 +133,106 @@ public sealed class NewClass : TypeSurrogate if ((field.AccessFlags & FieldAccessFlags.Public) != 0) attributes |= FieldAttributes.Public; if ((field.AccessFlags & FieldAccessFlags.Private) != 0) attributes |= FieldAttributes.Private; if ((field.AccessFlags & FieldAccessFlags.Protected) != 0) attributes |= FieldAttributes.Family; - var type = _builder.MakeType(field.Type); - var fieldInfo = Type.DefineField(field.Name, type.Type, attributes); + var type = Builder.MakeType(field.Type); + var fieldInfo = TypeBuilder.DefineField(field.Name, type.Type, attributes); fields.Add(field.Name, fieldInfo); } _fields = fields.ToFrozenDictionary(); } +} + +public abstract class TypeSurrogate : NewClass +{ + public abstract override Type Type { get; } + private readonly ConcurrentDictionary _methods = new(); + + protected TypeSurrogate(AssemblyBuilder builder, Class original) : base(builder, Create(builder, original), original) + { + CreateFields(); + } + + public virtual MethodBase GetMethod(string name, MethodSignature signature) + { + Method? original = null; + var method = Methods.GetOrAdd((name, signature), _ => + { + original = Original.GetMethod(name, signature); + if (original == null) throw new KeyNotFoundException($"Could not find method `{name}`"); + + var attributes = MethodAttributes.Static; + if ((original.AccessFlags & MethodAccessFlags.Final) != 0) attributes |= MethodAttributes.Final; + if ((original.AccessFlags & MethodAccessFlags.Public) != 0) attributes |= MethodAttributes.Public; + if ((original.AccessFlags & MethodAccessFlags.Private) != 0) attributes |= MethodAttributes.Private; + if ((original.AccessFlags & MethodAccessFlags.Protected) != 0) attributes |= MethodAttributes.Family; + + signature = (MethodSignature) original.Type.SpecialClassMetadata!; + var parameters = signature.ParamTypes.Select(t => Builder.MakeType(t).Type).ToList(); + if ((original.AccessFlags & MethodAccessFlags.Static) == 0) + parameters.Insert(0, Type); + + var method = TypeBuilder.DefineMethod( + name, attributes, CallingConventions.Standard, + Builder.MakeType(signature.ReturnType).Type, + parameters.ToArray() + ); + + _methods[method] = (original.AccessFlags & MethodAccessFlags.Static) != 0; + return method; + }); + + if (original is null) + return method; + + if (!original.TryGetCode(out var code, out var reader)) + return method; + + var methodBuilder = (MethodBuilder) method; + Builder.CompileOpCodes(Original, method, methodBuilder.GetILGenerator(), code, reader); + return method; + } + + internal override Type CallMethod(string name, MethodSignature signature, Stack stack, ILGenerator il) + { + var method = GetMethod(name, signature); + if (!_methods[method]) stack.Pop(); + for (var i = 0; i < signature.ParamTypes.Count; i++) stack.Pop(); + + Type type; + switch (method) + { + case MethodInfo info when method.IsVirtual: + il.Emit(OpCodes.Callvirt, info); + type = info.ReturnType; + break; + + case MethodInfo info: + il.Emit(OpCodes.Call, info); + type = info.ReturnType; + break; + + default: throw new NotImplementedException(); + } + + if(type != typeof(void)) + stack.Push(new Value(type)); + + return type; + } + + private static TypeBuilder Create(AssemblyBuilder builder, Class original) + { + TypeAttributes flags = default; + if ((original.AccessFlags & ClassAccessFlags.Public) != 0) flags |= TypeAttributes.Public; + if ((original.AccessFlags & ClassAccessFlags.Abstract) != 0) flags |= TypeAttributes.Abstract; + if ((original.AccessFlags & ClassAccessFlags.Final) != 0) flags |= TypeAttributes.Sealed; + if ((original.AccessFlags & ClassAccessFlags.Interface) != 0) flags |= TypeAttributes.Interface; + + var name = original.Namespace.IsEmpty + ? original.Name.ToString() + : $"{original.Namespace.ToString().Replace('/', '.')}.{original.Name}"; + + var type = builder.Module.DefineType(name, flags); + return type; + } } \ No newline at end of file diff --git a/CLI/Types/Type.cs b/CLI/Types/Type.cs index 1d2e611..f90cb77 100644 --- a/CLI/Types/Type.cs +++ b/CLI/Types/Type.cs @@ -4,15 +4,21 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types; -public abstract class TypeSurrogate +public abstract class TypeRepresentation { public abstract Type Type { get; } - public abstract FieldInfo GetField(string name); - public abstract MethodBase GetMethod(string name, MethodSignature signature); - public abstract Type CallMethod(string name, MethodSignature signature, ILGenerator il); - public MethodBase GetMethod(Method method) + internal virtual Value GetField(string name, ILGenerator il) { - return GetMethod(method.Name, (MethodSignature)method.Type.SpecialClassMetadata!); + throw new NotImplementedException(); } -} \ No newline at end of file + + internal virtual void SetField(string name, Value value, ILGenerator il) + { + throw new NotImplementedException(); + } + + public abstract MethodBase GetMethod(Method method); + + internal abstract Type CallMethod(string name, MethodSignature signature, Stack stack, ILGenerator il); +} diff --git a/CLI/Types/java/lang/Array.cs b/CLI/Types/java/lang/Array.cs index 14b6da4..11ba10c 100644 --- a/CLI/Types/java/lang/Array.cs +++ b/CLI/Types/java/lang/Array.cs @@ -1,60 +1,14 @@ -using System.Reflection; -using System.Reflection.Emit; using JCIL.Java.Class; namespace JCIL.CLI.Types.java.lang; -public class Array(Class javaClass) : TypeSurrogate +public class Array(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(System.Array); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } -public sealed class ArrayOf(TypeSurrogate elementType, Class javaClass) : Array(javaClass) +public sealed class ArrayOf(AssemblyBuilder builder, TypeRepresentation elementType, Class javaClass) : Array(builder, javaClass) { public override Type Type { get; } = elementType.Type.MakeArrayType(); - public readonly TypeSurrogate ElementType = elementType; - - private MethodInfo CloneMethod - => field ??= Type.GetMethod("Clone", BindingFlags.Instance | BindingFlags.Public)!; - - public override FieldInfo GetField(string name) - { - - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - switch (name, signature.ParamTypes) - { - case ("clone", []): - { - il.Emit(OpCodes.Callvirt, CloneMethod); - return Type; - } - } - throw new NotImplementedException(); - } + public readonly TypeRepresentation ElementType = elementType; } \ No newline at end of file diff --git a/CLI/Types/java/lang/Boolean.cs b/CLI/Types/java/lang/Boolean.cs index 31d752a..8d8a31e 100644 --- a/CLI/Types/java/lang/Boolean.cs +++ b/CLI/Types/java/lang/Boolean.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Boolean(Class javaClass) : TypeSurrogate +public sealed class Boolean(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(bool); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Byte.cs b/CLI/Types/java/lang/Byte.cs index c178c6f..177a9e3 100644 --- a/CLI/Types/java/lang/Byte.cs +++ b/CLI/Types/java/lang/Byte.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Byte(Class javaClass) : TypeSurrogate +public sealed class Byte(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(byte); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Char.cs b/CLI/Types/java/lang/Char.cs index cf9441a..ba502a4 100644 --- a/CLI/Types/java/lang/Char.cs +++ b/CLI/Types/java/lang/Char.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Char(Class javaClass) : TypeSurrogate +public sealed class Char(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(char); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Double.cs b/CLI/Types/java/lang/Double.cs index 41d6774..3a4aa1f 100644 --- a/CLI/Types/java/lang/Double.cs +++ b/CLI/Types/java/lang/Double.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Double(Class javaClass) : TypeSurrogate +public sealed class Double(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(double); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Float.cs b/CLI/Types/java/lang/Float.cs index 41c1114..0fba1f4 100644 --- a/CLI/Types/java/lang/Float.cs +++ b/CLI/Types/java/lang/Float.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Float(Class javaClass) : TypeSurrogate +public sealed class Float(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(float); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Integer.cs b/CLI/Types/java/lang/Integer.cs index 30d24eb..1b26761 100644 --- a/CLI/Types/java/lang/Integer.cs +++ b/CLI/Types/java/lang/Integer.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Integer(Class javaClass) : TypeSurrogate +public sealed class Integer(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(int); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Long.cs b/CLI/Types/java/lang/Long.cs index 65cad1d..d501e89 100644 --- a/CLI/Types/java/lang/Long.cs +++ b/CLI/Types/java/lang/Long.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Long(Class javaClass) : TypeSurrogate +public sealed class Long(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(long); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Object.cs b/CLI/Types/java/lang/Object.cs new file mode 100644 index 0000000..a9fcf24 --- /dev/null +++ b/CLI/Types/java/lang/Object.cs @@ -0,0 +1,8 @@ +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Object(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) +{ + public override Type Type => typeof(object); +} \ No newline at end of file diff --git a/CLI/Types/java/lang/Short.cs b/CLI/Types/java/lang/Short.cs index bf4c261..c1dd492 100644 --- a/CLI/Types/java/lang/Short.cs +++ b/CLI/Types/java/lang/Short.cs @@ -4,23 +4,7 @@ using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Short(Class javaClass) : TypeSurrogate +public sealed class Short(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(short); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/CLI/Types/java/lang/String.cs b/CLI/Types/java/lang/String.cs index f444e8e..2e9ba16 100644 --- a/CLI/Types/java/lang/String.cs +++ b/CLI/Types/java/lang/String.cs @@ -1,26 +1,12 @@ -using System.Reflection; -using System.Reflection.Emit; using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class String(Class javaClass) : TypeSurrogate +public sealed class String : TypeSurrogate { public override Type Type => typeof(string); - public readonly Class Original = javaClass; - public override FieldInfo GetField(string name) + public String(AssemblyBuilder builder, Class javaClass) : base(builder, javaClass) { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); } } \ No newline at end of file diff --git a/CLI/Types/java/lang/Void.cs b/CLI/Types/java/lang/Void.cs index b01d483..8a4d3d3 100644 --- a/CLI/Types/java/lang/Void.cs +++ b/CLI/Types/java/lang/Void.cs @@ -1,26 +1,8 @@ -using System.Reflection; -using System.Reflection.Emit; using JCIL.Java.Class; namespace JCIL.CLI.Types.Java.Lang; -public sealed class Void(Class javaClass) : TypeSurrogate +public sealed class Void(AssemblyBuilder builder, Class javaClass) : TypeSurrogate(builder, javaClass) { public override Type Type => typeof(void); - public readonly Class Original = javaClass; - - public override FieldInfo GetField(string name) - { - throw new NotImplementedException(); - } - - public override MethodBase GetMethod(string name, MethodSignature signature) - { - throw new NotImplementedException(); - } - - public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/JCIL.Core/ILHelpers.cs b/JCIL.Core/ILHelpers.cs index df59bf4..4ef716a 100755 --- a/JCIL.Core/ILHelpers.cs +++ b/JCIL.Core/ILHelpers.cs @@ -11,4 +11,22 @@ public static class ILHelpers if (a < b) return -1; return 0; } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static int FCmpL(float a, float b) + { + if (float.IsNaN(a) || float.IsNaN(b)) return -1; + if (a > b) return 1; + if (a < b) return -1; + return 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static int FCmpG(float a, float b) + { + if (float.IsNaN(a) || float.IsNaN(b)) return 1; + if (a > b) return 1; + if (a < b) return -1; + return 0; + } } \ No newline at end of file diff --git a/JavaClass/Method.cs b/JavaClass/Method.cs index c6b3156..9ecd6e4 100755 --- a/JavaClass/Method.cs +++ b/JavaClass/Method.cs @@ -48,13 +48,15 @@ public sealed record MethodSignature(Class ReturnType, IReadOnlyList Para public sealed class Method { + public readonly Class DeclaringClass; public readonly MethodAccessFlags AccessFlags; public readonly string Name; public readonly Class Type; public readonly IReadOnlyList Attributes; - private Method(MethodAccessFlags flags, string name, Class type, Attribute[] attributes) + private Method(Class declaringClass, MethodAccessFlags flags, string name, Class type, Attribute[] attributes) { + DeclaringClass = declaringClass; AccessFlags = flags; Name = name; Type = type; @@ -75,7 +77,7 @@ public sealed class Method var attributes = new Attribute[attributesCount]; foreach (ref var attribute in attributes.AsSpan()) attribute = Attribute.Read(parent, reader); - return new Method(flags, name, descriptorClass, attributes); + return new Method(parent, flags, name, descriptorClass, attributes); } public bool TryGetCode([MaybeNullWhen(false)] out Code code, out OpCodeReader opcodes)