From c80dd836ef393febf2f296aaf2b6722a8bf5772c Mon Sep 17 00:00:00 2001 From: Mia Date: Wed, 6 May 2026 03:06:31 +0200 Subject: [PATCH] More crap --- CLI/AssemblyBuilder.cs | 1077 ++++++++++++++------------------ CLI/Program.cs | 14 +- CLI/Types/New.cs | 116 ++++ CLI/Types/Type.cs | 18 + CLI/Types/java/lang/Array.cs | 49 ++ CLI/Types/java/lang/Boolean.cs | 26 + CLI/Types/java/lang/Byte.cs | 26 + CLI/Types/java/lang/Char.cs | 26 + CLI/Types/java/lang/Double.cs | 26 + CLI/Types/java/lang/Float.cs | 26 + CLI/Types/java/lang/Integer.cs | 26 + CLI/Types/java/lang/Long.cs | 26 + CLI/Types/java/lang/String.cs | 26 + CLI/Types/java/lang/Void.cs | 26 + JavaClass/Class.cs | 5 + JavaClass/Method.cs | 20 + 16 files changed, 923 insertions(+), 610 deletions(-) create mode 100644 CLI/Types/New.cs create mode 100644 CLI/Types/Type.cs create mode 100644 CLI/Types/java/lang/Array.cs create mode 100644 CLI/Types/java/lang/Boolean.cs create mode 100644 CLI/Types/java/lang/Byte.cs create mode 100644 CLI/Types/java/lang/Char.cs create mode 100644 CLI/Types/java/lang/Double.cs create mode 100644 CLI/Types/java/lang/Float.cs create mode 100644 CLI/Types/java/lang/Integer.cs create mode 100644 CLI/Types/java/lang/Long.cs create mode 100644 CLI/Types/java/lang/String.cs create mode 100644 CLI/Types/java/lang/Void.cs diff --git a/CLI/AssemblyBuilder.cs b/CLI/AssemblyBuilder.cs index 26aa2a4..553c3b5 100755 --- a/CLI/AssemblyBuilder.cs +++ b/CLI/AssemblyBuilder.cs @@ -2,8 +2,10 @@ using System.Collections.Concurrent; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; -using JCIL.Core; +using JCIL.CLI.Types; +using JCIL.CLI.Types.java.lang; using JCIL.Java.Class; +using OpCode = JCIL.Java.Class.OpCode; namespace JCIL.CLI; @@ -11,10 +13,9 @@ public sealed class AssemblyBuilder { public readonly System.Reflection.Emit.AssemblyBuilder Assembly; private readonly ModuleBuilder _module; - private readonly ConcurrentDictionary _classes = []; + private readonly ConcurrentDictionary _classes = []; private readonly ConcurrentDictionary _fields = []; private readonly ConcurrentDictionary _methods = []; - private readonly MethodInfo _getType; public int CompiledMethods { get; private set; } public int TotalMethods => _methods.Count; @@ -26,578 +27,376 @@ public sealed class AssemblyBuilder : new PersistedAssemblyBuilder(name, typeof(int).Assembly); _module = Assembly.DefineDynamicModule("JCIL_Output"); - _getType = typeof(object).GetMethod(nameof(object.GetType), BindingFlags.Instance | BindingFlags.Public)!; } - public void ForceBuildMethod(Method method) + public TypeSurrogate MakeType(Class javaClass) { - _ = _methods[method].Method; - } + if (_classes.TryGetValue(javaClass, out var type)) + return type; - public Type Build(Class javaClass) - { switch (javaClass.SpecialClassMetadata) { - case IntrinsicMetadata { Type: IntrinsicType.Boolean }: return typeof(bool); - case IntrinsicMetadata { Type: IntrinsicType.Byte }: return typeof(byte); - case IntrinsicMetadata { Type: IntrinsicType.Char }: return typeof(char); - case IntrinsicMetadata { Type: IntrinsicType.Short }: return typeof(short); - case IntrinsicMetadata { Type: IntrinsicType.Int }: return typeof(int); - case IntrinsicMetadata { Type: IntrinsicType.Long }: return typeof(long); - case IntrinsicMetadata { Type: IntrinsicType.Float }: return typeof(float); - case IntrinsicMetadata { Type: IntrinsicType.Double }: return typeof(double); - case IntrinsicMetadata { Type: IntrinsicType.Void }: return typeof(void); - case ArrayMetadata { ElementClass: var elementClass }: return Build(elementClass).MakeArrayType(); + case IntrinsicMetadata intrinsic: + { + switch (intrinsic.Type) + { + case IntrinsicType.Byte: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Byte(c)); + case IntrinsicType.Char: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Char(c)); + case IntrinsicType.Int: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Integer(c)); + case IntrinsicType.Long: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Long(c)); + case IntrinsicType.Boolean: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Boolean(c)); + case IntrinsicType.Float: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Float(c)); + case IntrinsicType.Double: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Double(c)); + case IntrinsicType.Void: + return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Void(c)); + default: throw new NotImplementedException($"Unimplemented Intrinsic {intrinsic.Type}"); + } + } + case ArrayMetadata array: + return _classes.GetOrAdd(javaClass, c => new ArrayOf(MakeType(array.ElementClass), c)); } - var build = false; - var theType = _classes.GetOrAdd(javaClass, _ => + var newClass = (NewClass)_classes.GetOrAdd(javaClass, _ => { TypeAttributes flags = default; if ((javaClass.AccessFlags & ClassAccessFlags.Public) != 0) flags |= TypeAttributes.Public; if ((javaClass.AccessFlags & ClassAccessFlags.Abstract) != 0) flags |= TypeAttributes.Abstract; if ((javaClass.AccessFlags & ClassAccessFlags.Final) != 0) flags |= TypeAttributes.Sealed; if ((javaClass.AccessFlags & ClassAccessFlags.Interface) != 0) flags |= TypeAttributes.Interface; - - var name = javaClass.Namespace.Length == 0 - ? javaClass.Name.ToString() - : $"{javaClass.Namespace.ToString().Replace('/', '.')}.{javaClass.Name}"; - - build = true; - return _module.DefineType(name, flags); + var builder = _module.DefineType(javaClass.Name.ToString(), flags); + return new NewClass(this, builder, javaClass); }); - - if (build && theType is TypeBuilder typeBuilder) - { - // Console.WriteLine($"Building type `{typeBuilder.FullName}`"); - - if (!theType.IsInterface && javaClass.SuperClass is not null && javaClass.SuperClass != javaClass) - typeBuilder.SetParent(Build(javaClass.SuperClass)); - - foreach (var field in javaClass.Fields) - { - FieldAttributes flags = default; - if ((field.AccessFlags & FieldAccessFlags.Static) != 0) flags |= FieldAttributes.Static; - if ((field.AccessFlags & FieldAccessFlags.Final) != 0) flags |= FieldAttributes.InitOnly; - if ((field.AccessFlags & FieldAccessFlags.Public) != 0) flags |= FieldAttributes.Public; - if ((field.AccessFlags & FieldAccessFlags.Private) != 0) flags |= FieldAttributes.Private; - if ((field.AccessFlags & FieldAccessFlags.Protected) != 0) flags |= FieldAttributes.Family; - - var type = Build(field.Type); - _fields.TryAdd(field, typeBuilder.DefineField(field.Name, type, flags)); - } - - foreach (var method in javaClass.Methods) - { - MethodAttributes flags = default; - if ((method.AccessFlags & MethodAccessFlags.Static) != 0) flags |= MethodAttributes.Static; - else flags |= MethodAttributes.Virtual; - if ((method.AccessFlags & MethodAccessFlags.Public) != 0) flags |= MethodAttributes.Public; - if ((method.AccessFlags & MethodAccessFlags.Private) != 0) flags |= MethodAttributes.Private; - if ((method.AccessFlags & MethodAccessFlags.Protected) != 0) flags |= MethodAttributes.Family; - if ((method.AccessFlags & MethodAccessFlags.Abstract) != 0) flags |= MethodAttributes.Abstract; - if ((method.AccessFlags & MethodAccessFlags.Final) != 0) flags |= MethodAttributes.Final; - - var signature = (MethodSignature)method.Type.SpecialClassMetadata!; - var paramTypes = signature.ParamTypes.Select(Build).ToArray(); - if (method.Name == "") - { - flags &= ~MethodAttributes.Virtual; - var ctor = typeBuilder.DefineConstructor(flags, CallingConventions.Standard, paramTypes); - if (method.Attributes.OfType().FirstOrDefault() is { } attrib) - { - for (var j = 0; j < attrib.Parameters.Count; j++) - { - // TODO populate parameter attributes - ctor.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1); - } - } - - _methods.TryAdd(method, new LazyMethod(ctor, () => - { - Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`"); - if (method.TryGetCode(out var code, out var opcodes)) - CompileOpCodes(javaClass, ctor, ctor.GetILGenerator(), code, opcodes); - })); - } - else if (method.Name == "") - { - var ctor = typeBuilder.DefineTypeInitializer(); - if (method.Attributes.OfType().FirstOrDefault() is { } attrib) - { - for (var j = 0; j < attrib.Parameters.Count; j++) - { - // TODO populate parameter attributes - ctor.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1); - } - } - - _methods.TryAdd(method, new LazyMethod(ctor, () => - { - Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`"); - if (method.TryGetCode(out var code, out var opcodes)) - CompileOpCodes(javaClass, ctor, ctor.GetILGenerator(), code, opcodes); - })); - } - else - { - var methodBuilder = typeBuilder.DefineMethod(method.Name, flags); - methodBuilder.SetReturnType(Build(signature.ReturnType)); - methodBuilder.SetParameters(paramTypes); - - if (method.Attributes.OfType().FirstOrDefault() is { } attrib) - { - for (var j = 0; j < attrib.Parameters.Count; j++) - { - // TODO populate parameter attributes - methodBuilder.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1); - } - } - - _methods.TryAdd(method, new LazyMethod(methodBuilder, () => - { - Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`"); - if (method.TryGetCode(out var code, out var opcodes)) - CompileOpCodes(javaClass, methodBuilder, methodBuilder.GetILGenerator(), code, opcodes); - })); - } - } - - return typeBuilder.CreateType(); - } - - foreach (var interfaceClass in javaClass.Interfaces) - _ = Build(interfaceClass); - - return theType; + newClass.CreateFields(); + return newClass; } - private void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, OpCodeReader opcodes) + internal void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, + OpCodeReader opcodes) { - var locals = new (Type, int)[code.MaxLocals]; - var localsCount = 0; - var localsIdx = 0; - - if (!methodBase.IsStatic) - { - il.DeclareLocal(methodBase.DeclaringType!); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Stloc_0); - locals[localsIdx++] = (methodBase.DeclaringType!, localsCount++); - } - - var parameters = methodBase.GetParameters(); - for (int i = 0, j = methodBase.IsStatic ? 0 : 1; i < parameters.Length; i++, j++) - { - il.DeclareLocal(parameters[i].ParameterType); - il.Emit(OpCodes.Ldarg, (short) j); - il.Emit(OpCodes.Stloc, (short) j); - locals[localsIdx++] = (parameters[i].ParameterType, localsCount++); - } - - if (code.Attributes.OfType().FirstOrDefault() is not { Frames: var frames }) - frames = []; + var thisType = methodBase.DeclaringType!; + Debug.Writer.WriteLine($"Compiling {thisType.FullName}.{methodBase.Name}"); + Debug.Writer.Indent++; + var labels = CreateLabels(il, opcodes); var position = opcodes.Reader.Stream.Position; - var labels = new Dictionary(); - while (opcodes.MoveNext()) + var stack = new Stack(code.MaxStack); + var locals = new Value[code.MaxLocals]; + var localCount = 0; + { - labels.Add(position, il.DefineLabel()); - position = opcodes.Reader.Stream.Position; + var idx = 0; + if (!methodBase.IsStatic) + locals[idx] = Value.Parameter(thisType, idx++); + + foreach (var parameter in methodBase.GetParameters()) + locals[idx] = Value.Parameter(parameter.ParameterType, idx++); } - opcodes.Reset(); - position = opcodes.Reader.Stream.Position; - - void AddLocal(in StackMapTable.VerificationTypeInfo info) + void CompileOpCode(long position, OpCode opcode) { - Type compiledType; - switch (info.Tag) - { - case StackMapTable.VerificationTypeInfoTag.Top: - locals[localsIdx++] = default; - return; - - case StackMapTable.VerificationTypeInfoTag.Integer: compiledType = typeof(int); break; - case StackMapTable.VerificationTypeInfoTag.Long: compiledType = typeof(long); break; - case StackMapTable.VerificationTypeInfoTag.Float: compiledType = typeof(float); break; - case StackMapTable.VerificationTypeInfoTag.Double: compiledType = typeof(double); break; - - case StackMapTable.VerificationTypeInfoTag.Object: - { - var type = (Class)javaClass.GetConstant(info.Parameter)!; - compiledType = Build(type); - break; - } - - case StackMapTable.VerificationTypeInfoTag.UninitializedThis: - compiledType = methodBase.DeclaringType!; - break; - - default: throw new NotImplementedException($"Unimplemented local tag {info.Tag}"); - } - - locals[localsIdx++] = (compiledType, localsCount++); - il.DeclareLocal(compiledType); - } - - for (var f = -1; opcodes.MoveNext();) - { - if (f + 1 < frames.Count && position >= frames[f + 1].StartOffset) - { - switch (frames[++f]) - { - case StackMapTable.SameFrame: break; - case StackMapTable.SameFrameExtended: break; - case StackMapTable.OneLocalFrame: break; - case StackMapTable.OneLocalFrameExtended: break; - - case StackMapTable.AppendFrame frame: - foreach (var info in frame.Locals) AddLocal(info); - break; - - case StackMapTable.ChopFrame frame: - localsIdx -= frame.Count; - break; - - case StackMapTable.FullFrame frame: - { - localsIdx = 0; - foreach (var info in frame.Locals) AddLocal(info); - break; - } - - default: throw new NotImplementedException($"Unimplemented {frames[f]}"); - } - } - - il.MarkLabel(labels[position]); - var opcode = opcodes.Current; + Debug.Writer.WriteLine($"IL_{position:X04}: {opcode.ToString(javaClass)}"); Label JmpLabel() { return labels[position + (short)opcode.P0.UShort]; } - void LoadLocal(int idx) + void CheckCast(Value value, Type type) { - var loc = locals[idx].Item2; - switch (loc) + if (value.Type == typeof(int) && type == typeof(byte)) { - case 0: il.Emit(OpCodes.Ldloc_0); break; - case 1: il.Emit(OpCodes.Ldloc_1); break; - case 2: il.Emit(OpCodes.Ldloc_2); break; - case 3: il.Emit(OpCodes.Ldloc_3); break; - case <= 255: il.Emit(OpCodes.Ldloc_S, (byte)loc); break; - default: il.Emit(OpCodes.Ldloc, (short)loc); break; + il.Emit(OpCodes.Conv_I1); } - } - - void StoreLocal(Type ty, int idx) - { - ref var loc = ref locals[idx]; - if (loc.Item2 == -1 || loc.Item1 is null || (loc.Item1 != ty && (loc.Item1.IsValueType || ty.IsValueType))) + else if (!value.Type.IsAssignableTo(type)) { - il.DeclareLocal(ty); - loc = (ty, localsCount++); - } - - switch (loc.Item2) - { - case 0: il.Emit(OpCodes.Stloc_0); break; - case 1: il.Emit(OpCodes.Stloc_1); break; - case 2: il.Emit(OpCodes.Stloc_2); break; - case 3: il.Emit(OpCodes.Stloc_3); break; - case <= 255: il.Emit(OpCodes.Stloc_S, (byte)loc.Item2); break; - default: il.Emit(OpCodes.Stloc, (short)loc.Item2); break; + throw new InvalidOperationException( + $"Value of type {value.Type} cannot be assigned to one of type {type}."); } } switch (opcode.Mnemonic) { - case OpCodeMnemonic.Nop: break; - case OpCodeMnemonic.AConstNull: il.Emit(OpCodes.Ldnull); break; - case OpCodeMnemonic.IConstM1: il.Emit(OpCodes.Ldc_I4_M1); break; - case OpCodeMnemonic.IConst0: il.Emit(OpCodes.Ldc_I4_0); break; - case OpCodeMnemonic.IConst1: il.Emit(OpCodes.Ldc_I4_1); break; - case OpCodeMnemonic.IConst2: il.Emit(OpCodes.Ldc_I4_2); break; - case OpCodeMnemonic.IConst3: il.Emit(OpCodes.Ldc_I4_3); break; - case OpCodeMnemonic.IConst4: il.Emit(OpCodes.Ldc_I4_4); break; - case OpCodeMnemonic.IConst5: il.Emit(OpCodes.Ldc_I4_5); break; - case OpCodeMnemonic.LConst0: il.Emit(OpCodes.Ldc_I8, 0L); break; - case OpCodeMnemonic.LConst1: il.Emit(OpCodes.Ldc_I8, 1L); break; - case OpCodeMnemonic.FConst0: il.Emit(OpCodes.Ldc_R4, 0f); break; - case OpCodeMnemonic.FConst1: il.Emit(OpCodes.Ldc_R4, 1f); break; - case OpCodeMnemonic.FConst2: il.Emit(OpCodes.Ldc_R4, 2f); break; - case OpCodeMnemonic.DConst0: il.Emit(OpCodes.Ldc_R4, 0d); break; - case OpCodeMnemonic.DConst1: il.Emit(OpCodes.Ldc_R4, 1d); break; + case OpCodeMnemonic.AConstNull: + stack.Push(new Value(typeof(object))); + il.Emit(OpCodes.Ldnull); + break; - case OpCodeMnemonic.PushByte: il.Emit(OpCodes.Ldc_I4, (int)opcode.P0.Byte); break; - case OpCodeMnemonic.PushShort: il.Emit(OpCodes.Ldc_I4, (int)(short)opcode.P0.UShort); break; + case OpCodeMnemonic.IConst0: + case OpCodeMnemonic.IConst1: + case OpCodeMnemonic.IConst2: + case OpCodeMnemonic.IConst3: + case OpCodeMnemonic.IConst4: + case OpCodeMnemonic.IConst5: + stack.Push(new Value(opcode.Mnemonic - OpCodeMnemonic.IConst0)); + 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.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); + var constant = javaClass.GetConstant(opcode.P0.UShort); switch (constant) { - //TODO this is wrong - case Class c: il.Emit(OpCodes.Ldtoken, Build(c)); break; - - case int val: il.Emit(OpCodes.Ldc_I4, val); break; - case short val: il.Emit(OpCodes.Ldc_I4, (int) val); break; - case long val: il.Emit(OpCodes.Ldc_I8, val); break; - case float val: il.Emit(OpCodes.Ldc_R4, val); break; - case double val: il.Emit(OpCodes.Ldc_R8, val); break; - - case string str: il.Emit(OpCodes.Ldstr, str); break; + case int 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: + { throw new NotImplementedException( - $"Unimplemented ldc {constant?.GetType().FullName ?? "null"}"); + $"Unknown opcode: {opcode.Mnemonic} {constant.GetType()}"); + } } break; } case OpCodeMnemonic.ILoad: - case OpCodeMnemonic.LLoad: - case OpCodeMnemonic.ALoad: LoadLocal(opcode.P0.Byte); break; - case OpCodeMnemonic.ILoad0: - case OpCodeMnemonic.LLoad0: - case OpCodeMnemonic.FLoad0: - case OpCodeMnemonic.DLoad0: - case OpCodeMnemonic.ALoad0: LoadLocal(0); break; + case OpCodeMnemonic.ILoad1: + case OpCodeMnemonic.ILoad2: + case OpCodeMnemonic.ILoad3: + { + var idx = opcode.Mnemonic switch + { + OpCodeMnemonic.ILoad0 => 0, + OpCodeMnemonic.ILoad1 => 1, + OpCodeMnemonic.ILoad2 => 2, + OpCodeMnemonic.ILoad3 => 3, + OpCodeMnemonic.ILoad => opcode.P0.UShort, + _ => -1, + }; + + if (locals[idx].Type != typeof(int)) + throw new InvalidOperationException($"Local {idx} is not an integer."); + + stack.Push(locals[idx].Load(il)); + break; + } + + case OpCodeMnemonic.ALoad: + case OpCodeMnemonic.ALoad0: case OpCodeMnemonic.ALoad1: - case OpCodeMnemonic.LLoad1: - case OpCodeMnemonic.FLoad1: - case OpCodeMnemonic.DLoad1: - case OpCodeMnemonic.ILoad1: LoadLocal(1); break; case OpCodeMnemonic.ALoad2: - case OpCodeMnemonic.LLoad2: - case OpCodeMnemonic.FLoad2: - case OpCodeMnemonic.DLoad2: - case OpCodeMnemonic.ILoad2: LoadLocal(2); break; case OpCodeMnemonic.ALoad3: - case OpCodeMnemonic.LLoad3: - case OpCodeMnemonic.FLoad3: - case OpCodeMnemonic.DLoad3: - case OpCodeMnemonic.ILoad3: LoadLocal(3); break; - - case OpCodeMnemonic.IALoad: il.Emit(OpCodes.Ldelem_I4); break; - case OpCodeMnemonic.LALoad: il.Emit(OpCodes.Ldelem_I8); break; - case OpCodeMnemonic.AALoad: il.Emit(OpCodes.Ldelem_Ref); break; - case OpCodeMnemonic.BALoad: il.Emit(OpCodes.Ldelem_I1); break; - case OpCodeMnemonic.CALoad: il.Emit(OpCodes.Ldelem_I2); break; - case OpCodeMnemonic.SALoad: il.Emit(OpCodes.Ldelem_I2); break; - - case OpCodeMnemonic.AStore: StoreLocal(typeof(object), opcode.P0.Byte); break; - case OpCodeMnemonic.IStore: StoreLocal(typeof(int), opcode.P0.Byte); break; - case OpCodeMnemonic.LStore: StoreLocal(typeof(long), opcode.P0.Byte); break; - case OpCodeMnemonic.FStore: StoreLocal(typeof(float), opcode.P0.Byte); break; - case OpCodeMnemonic.DStore: StoreLocal(typeof(double), opcode.P0.Byte); break; - - case OpCodeMnemonic.AStore0: StoreLocal(typeof(object), 0); break; - case OpCodeMnemonic.IStore0: StoreLocal(typeof(int), 0); break; - case OpCodeMnemonic.LStore0: StoreLocal(typeof(long), 0); break; - case OpCodeMnemonic.AStore1:StoreLocal(typeof(object), 1); break; - case OpCodeMnemonic.IStore1:StoreLocal(typeof(int), 1); break; - case OpCodeMnemonic.LStore1:StoreLocal(typeof(long), 1); break; - case OpCodeMnemonic.AStore2:StoreLocal(typeof(object), 2); break; - case OpCodeMnemonic.IStore2:StoreLocal(typeof(int), 2); break; - case OpCodeMnemonic.LStore2:StoreLocal(typeof(long), 2); break; - case OpCodeMnemonic.AStore3:StoreLocal(typeof(object), 3); break; - case OpCodeMnemonic.IStore3:StoreLocal(typeof(int), 3); break; - case OpCodeMnemonic.LStore3:StoreLocal(typeof(long), 3); break; - - case OpCodeMnemonic.LAStore: il.Emit(OpCodes.Stelem_I8); break; - case OpCodeMnemonic.AAStore: il.Emit(OpCodes.Stelem_Ref); break; - case OpCodeMnemonic.BAStore: il.Emit(OpCodes.Stelem_I1); break; - case OpCodeMnemonic.CAStore: il.Emit(OpCodes.Stelem_I2); break; - - - case OpCodeMnemonic.Pop: il.Emit(OpCodes.Pop); break; - case OpCodeMnemonic.Pop2: { - var class1Label = il.DefineLabel(); - var class2Label = il.DefineLabel(); - var continueLabel = il.DefineLabel(); + var idx = opcode.Mnemonic switch + { + OpCodeMnemonic.ALoad0 => 0, + OpCodeMnemonic.ALoad1 => 1, + OpCodeMnemonic.ALoad2 => 2, + OpCodeMnemonic.ALoad3 => 3, + OpCodeMnemonic.ALoad => opcode.P0.UShort, + _ => -1, + }; + + if (locals[idx] is not { Type.IsClass: true }) + throw new InvalidOperationException($"Local {idx} is not a class."); + + stack.Push(locals[idx].Load(il)); + break; + } + + case OpCodeMnemonic.AALoad: + { + var value = stack.Pop(); + + if (!value.Type.IsArray) + throw new InvalidOperationException("Value is not an array."); + + if (value.Type.GetElementType() is not { IsClass: true } elementType) + throw new InvalidOperationException("Array element is not an object."); + + il.Emit(OpCodes.Ldelem_Ref, elementType); + stack.Push(new Value(elementType)); + break; + } + + case OpCodeMnemonic.LStore: + case OpCodeMnemonic.LStore0: + case OpCodeMnemonic.LStore1: + case OpCodeMnemonic.LStore2: + case OpCodeMnemonic.LStore3: + { + var value = stack.Pop(); + CheckCast(value, typeof(long)); + ref var loc = ref locals[opcode.Mnemonic switch + { + OpCodeMnemonic.LStore => opcode.P0.UShort, + _ => opcode.Mnemonic - OpCodeMnemonic.LStore0, + }]; + if (loc.Type != typeof(long)) + { + il.DeclareLocal(typeof(long)); + loc = Value.Local(typeof(long), localCount++); + } + loc.Store(il); + break; + } + + case OpCodeMnemonic.Pop: + { + stack.Pop(); + il.Emit(OpCodes.Pop); + break; + } + + case OpCodeMnemonic.Dup: + { + var value = stack.Pop(); + stack.Push(value); + stack.Push(value); il.Emit(OpCodes.Dup); - il.EmitCall(OpCodes.Call, _getType, null); - il.Emit(OpCodes.Dup); - il.Emit(OpCodes.Ldtoken, typeof(long).MetadataToken); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Beq, class2Label); - il.Emit(OpCodes.Ldtoken, typeof(double).MetadataToken); - il.Emit(OpCodes.Beq, class2Label); - il.MarkLabel(class1Label); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Br, continueLabel); - il.MarkLabel(class2Label); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Br, continueLabel); - il.MarkLabel(continueLabel); - break; - } - case OpCodeMnemonic.Dup: il.Emit(OpCodes.Dup); break; - - case OpCodeMnemonic.IAdd: - case OpCodeMnemonic.LAdd: - case OpCodeMnemonic.FAdd: - case OpCodeMnemonic.DAdd: il.Emit(OpCodes.Add); break; - case OpCodeMnemonic.ISub: - case OpCodeMnemonic.LSub: - case OpCodeMnemonic.FSub: - case OpCodeMnemonic.DSub: il.Emit(OpCodes.Sub); break; - case OpCodeMnemonic.IMul: - case OpCodeMnemonic.LMul: - case OpCodeMnemonic.FMul: - case OpCodeMnemonic.DMul: il.Emit(OpCodes.Mul); break; - case OpCodeMnemonic.IDiv: - case OpCodeMnemonic.LDiv: - case OpCodeMnemonic.FDiv: - case OpCodeMnemonic.DDiv: il.Emit(OpCodes.Div); break; - case OpCodeMnemonic.IRem: - case OpCodeMnemonic.LRem: - case OpCodeMnemonic.FRem: - case OpCodeMnemonic.DRem: il.Emit(OpCodes.Rem); break; - case OpCodeMnemonic.INeg: - case OpCodeMnemonic.LNeg: il.Emit(OpCodes.Neg); break; - - case OpCodeMnemonic.IShl: - case OpCodeMnemonic.LShl: il.Emit(OpCodes.Shl); break; - case OpCodeMnemonic.IShr: - case OpCodeMnemonic.LShr: il.Emit(OpCodes.Shr); break; - case OpCodeMnemonic.IUShr: - case OpCodeMnemonic.LUShr: il.Emit(OpCodes.Shr_Un); break; - case OpCodeMnemonic.IAnd: - case OpCodeMnemonic.LAnd: il.Emit(OpCodes.And); break; - case OpCodeMnemonic.IOr: - case OpCodeMnemonic.LOr: il.Emit(OpCodes.Or); break; - - case OpCodeMnemonic.IInc: - { - LoadLocal(opcode.P0.UShort); - il.Emit(OpCodes.Ldc_I4, opcode.P0.Byte); - il.Emit(OpCodes.Add); - StoreLocal(typeof(int), opcode.P0.UShort); break; } - case OpCodeMnemonic.IntToLong: il.Emit(OpCodes.Conv_I8); break; - case OpCodeMnemonic.IntToByte: il.Emit(OpCodes.Conv_I1); break; - case OpCodeMnemonic.IntToChar: il.Emit(OpCodes.Conv_U2); break; - case OpCodeMnemonic.IntToShort: il.Emit(OpCodes.Conv_I2); break; - - case OpCodeMnemonic.LCmp: + case OpCodeMnemonic.IfEq: { - var func = ILHelpers.LCmp; - il.EmitCall(OpCodes.Call, func.Method, null); + var value = stack.Pop(); + if (value.Type != typeof(int) && value.Type != typeof(bool)) + throw new InvalidOperationException($"Expected integer, found {value.Type}."); + + il.Emit(OpCodes.Brfalse, JmpLabel()); break; } - case OpCodeMnemonic.InvokeSpecial: - case OpCodeMnemonic.InvokeStatic: + case OpCodeMnemonic.IfACmpNe: { - var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(methodRef.Class); + if (!stack.Pop().Type.IsClass || !stack.Pop().Type.IsClass) + throw new InvalidOperationException("Value is not an object."); - var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory()) - .SpecialClassMetadata!; + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Brfalse, JmpLabel()); + break; + } - var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature); - if (method is null || !_methods.TryGetValue(method, out var compiledMethod)) - throw new Exception($"Method {methodRef.Method.Name} not found"); + case OpCodeMnemonic.IReturn: + case OpCodeMnemonic.LReturn: + case OpCodeMnemonic.FReturn: + case OpCodeMnemonic.DReturn: + case OpCodeMnemonic.AReturn: + { + var value = stack.Pop(); + il.Emit(OpCodes.Ret); + break; + } - if (compiledMethod.Method is MethodInfo methodInfo) - il.EmitCall(OpCodes.Call, methodInfo, null); + case OpCodeMnemonic.Return: + { + il.Emit(OpCodes.Ret); + break; + } - if (compiledMethod.Method is ConstructorInfo constructorInfo) - il.Emit(OpCodes.Call, constructorInfo); + case OpCodeMnemonic.GetStaticField: + { + 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)); + break; + } + case OpCodeMnemonic.GetField: + { + 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}."); + + stack.Push(new Value(field).Load(il)); + break; + } + + case OpCodeMnemonic.SetField: + { + var fieldRef = javaClass.GetConstant(opcode.P0.UShort); + var parent = MakeType(fieldRef.Class); + var field = parent.GetField(fieldRef.Field.Name); + var value = stack.Pop(); + + CheckCast(value, field.FieldType); + il.Emit(OpCodes.Stfld, field); break; } case OpCodeMnemonic.InvokeVirtual: + case OpCodeMnemonic.InvokeSpecial: + case OpCodeMnemonic.InvokeStatic: { - var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(methodRef.Class); + var methodRef = javaClass.GetConstant(opcode.P0.UShort); + var parent = MakeType(methodRef.Class); var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory()) .SpecialClassMetadata!; - var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature); - if (method is null || !_methods.TryGetValue(method, out var compiledMethod)) - throw new Exception($"Method {methodRef.Method.Name} not found"); - - if (compiledMethod.Method is MethodInfo methodInfo) - il.EmitCall(OpCodes.Callvirt, methodInfo, null); - - break; - } - - case OpCodeMnemonic.InvokeInterface: - { - var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(methodRef.Class); - - var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory()) - .SpecialClassMetadata!; - - var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature); - if (method is null || !_methods.TryGetValue(method, out var compiledMethod)) - throw new Exception($"Method {methodRef.Method.Name} not found"); - - if (compiledMethod.Method is MethodInfo methodInfo) - il.EmitCall(OpCodes.Callvirt, methodInfo, null); - + stack.Push(new Value(parent.CallMethod(methodRef.Method.Name, signature, il))); break; } case OpCodeMnemonic.New: { - var obj = (Class)javaClass.GetConstant(opcode.P0.UShort)!; - var objT = Build(obj); - - // TODO Make this work with any number of parameters + 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) { opcodes.MoveNext(); - if (opcodes.Current.Mnemonic == OpCodeMnemonic.InvokeSpecial) + position = opcodes.Reader.Stream.Position; + while (opcodes.Current.Mnemonic != OpCodeMnemonic.InvokeSpecial) { - var methodRef = (MethodRef)javaClass.GetConstant(opcodes.Current.P0.UShort)!; - _ = Build(methodRef.Class); - - var signature = (MethodSignature)javaClass.Loader - .GetClass(methodRef.Method.Descriptor.AsMemory()) - .SpecialClassMetadata!; - - var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature); - if (method is null || !_methods.TryGetValue(method, out var compiledMethod)) - throw new Exception($"Method {methodRef.Method.Name} not found"); - - if (compiledMethod.Method is ConstructorInfo constructorInfo) - { - il.Emit(OpCodes.Newobj, constructorInfo); - break; - } + 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!; + + 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; var func = RuntimeHelpers.GetUninitializedObject; - il.Emit(OpCodes.Ldtoken, objT); + il.Emit(OpCodes.Ldtoken, type.Type); il.Emit(OpCodes.Call, func.Method); + stack.Push(new Value(type.Type)); break; } case OpCodeMnemonic.NewArray: { - il.Emit(OpCodes.Newarr, opcode.P0.Byte switch + var elementType = opcode.P0.Byte switch { 4 => typeof(bool), 5 => typeof(char), @@ -607,166 +406,68 @@ public sealed class AssemblyBuilder 9 => typeof(short), 10 => typeof(int), 11 => typeof(long), - _ => throw new NotImplementedException(nameof(opcode.P0)), - }); + _ => typeof(void), + }; + il.Emit(OpCodes.Newarr, elementType); + stack.Push(new Value(elementType.MakeArrayType())); break; } - case OpCodeMnemonic.ANewArray: + case OpCodeMnemonic.IfNull: { - var elem = (Class)javaClass.GetConstant(opcode.P0.UShort)!; - il.Emit(OpCodes.Newarr, Build(elem)); - break; - } + if (!stack.Pop().Type.IsClass) + throw new InvalidOperationException("Value is not an object."); - - case OpCodeMnemonic.IfEq: il.Emit(OpCodes.Brfalse, JmpLabel()); break; - case OpCodeMnemonic.IfNe: il.Emit(OpCodes.Brtrue, JmpLabel()); break; - case OpCodeMnemonic.IfLt: - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Blt, JmpLabel()); - break; - case OpCodeMnemonic.IfLe: - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ble, JmpLabel()); - break; - case OpCodeMnemonic.IfGe: - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Bge, JmpLabel()); - break; - case OpCodeMnemonic.IfGt: - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Bgt, JmpLabel()); - break; - - case OpCodeMnemonic.IfICmpEq: il.Emit(OpCodes.Beq, JmpLabel()); break; - case OpCodeMnemonic.IfICmpNe: il.Emit(OpCodes.Bne_Un, JmpLabel()); break; - case OpCodeMnemonic.IfICmpLt: il.Emit(OpCodes.Blt, JmpLabel()); break; - case OpCodeMnemonic.IfICmpLe: il.Emit(OpCodes.Ble, JmpLabel()); break; - case OpCodeMnemonic.IfICmpGe: il.Emit(OpCodes.Bge, JmpLabel()); break; - case OpCodeMnemonic.IfICmpGt: il.Emit(OpCodes.Bgt, JmpLabel()); break; - - case OpCodeMnemonic.IfACmpEq: - { - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Brtrue, JmpLabel()); - break; - } - - case OpCodeMnemonic.IfACmpNe: - { - il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, JmpLabel()); break; } - case OpCodeMnemonic.Goto: il.Emit(OpCodes.Br, JmpLabel()); break; - case OpCodeMnemonic.IfNull: il.Emit(OpCodes.Brfalse, JmpLabel()); break; - case OpCodeMnemonic.IfNonNull: il.Emit(OpCodes.Brtrue, JmpLabel()); break; + case OpCodeMnemonic.IfNonNull: + { + if (!stack.Pop().Type.IsClass) + throw new InvalidOperationException("Value is not an object."); - case OpCodeMnemonic.Return: - case OpCodeMnemonic.IReturn: - case OpCodeMnemonic.LReturn: - case OpCodeMnemonic.FReturn: - case OpCodeMnemonic.DReturn: - case OpCodeMnemonic.AReturn: - { - il.Emit(OpCodes.Ret); - break; - } - - case OpCodeMnemonic.SetField: - { - var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(fieldRef.Class); - var field = fieldRef.Class.GetField(fieldRef.Field.Name)!; - var compiledField = _fields[field]; - il.Emit(OpCodes.Stfld, compiledField); - break; - } - case OpCodeMnemonic.SetStaticField: - { - var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(fieldRef.Class); - var field = fieldRef.Class.GetField(fieldRef.Field.Name)!; - var compiledField = _fields[field]; - il.Emit(OpCodes.Stsfld, compiledField); - break; - } - - case OpCodeMnemonic.GetField: - { - var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(fieldRef.Class); - var field = fieldRef.Class.GetField(fieldRef.Field.Name)!; - var compiledField = _fields[field]; - il.Emit(OpCodes.Ldfld, compiledField); - break; - } - - case OpCodeMnemonic.GetStaticField: - { - var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!; - _ = Build(fieldRef.Class); - var field = fieldRef.Class.GetField(fieldRef.Field.Name)!; - var compiledField = _fields[field]; - il.Emit(OpCodes.Ldsfld, compiledField); - break; - } - - case OpCodeMnemonic.ArrayLen: - { - il.Emit(OpCodes.Ldlen); - break; - } - - case OpCodeMnemonic.AThrow: - { - il.Emit(OpCodes.Throw); - break; - } - - case OpCodeMnemonic.InstanceOf: - { - var type = Build((Class)javaClass.GetConstant(opcode.P0.UShort)!); - il.Emit(OpCodes.Isinst, type); - break; - } - - case OpCodeMnemonic.CheckCast: - { - var type = Build((Class)javaClass.GetConstant(opcode.P0.UShort)!); - il.Emit(OpCodes.Castclass, type); - break; - } - - case OpCodeMnemonic.MonitorEnter: - { - Action func = Monitor.Enter; - il.EmitCall(OpCodes.Call, func.Method, null); - break; - } - - case OpCodeMnemonic.MonitorExit: - { - Action func = Monitor.Exit; - il.EmitCall(OpCodes.Call, func.Method, null); + il.Emit(OpCodes.Brtrue, JmpLabel()); break; } default: { - il.Emit(OpCodes.Nop); + Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"Unknown opcode: {opcode.Mnemonic}"); + Console.ResetColor(); + il.Emit(OpCodes.Nop); break; } } + } + for (; opcodes.MoveNext();) + { + il.MarkLabel(labels[position]); + var opcode = opcodes.Current; + CompileOpCode(position, opcode); position = opcodes.Reader.Stream.Position; } + Debug.Writer.Indent--; CompiledMethods += 1; } + + private static Dictionary CreateLabels(ILGenerator il, OpCodeReader opcodes) + { + var start = opcodes.Reader.Stream.Position; + var position = start; + var labels = new Dictionary(); + while (opcodes.MoveNext()) + { + labels.Add(position, il.DefineLabel()); + position = opcodes.Reader.Stream.Position; + } + + opcodes.Reader.Stream.Position = start; + return labels; + } } internal class LazyMethod(MethodBase method, Action compile) @@ -786,4 +487,168 @@ internal class LazyMethod(MethodBase method, Action compile) return method; } } +} + +internal readonly struct Value +{ + public delegate Value LoadDelegate(ILGenerator il, OpCodeParameter data); + + public delegate void StoreDelegate(ILGenerator il, OpCodeParameter data); + + public readonly Type Type; + private LoadDelegate? _load { get; init; } + private StoreDelegate? _store { get; init; } + private OpCodeParameter _data { get; init; } + public static implicit operator Type(Value v) => v.Type; + + public Value Load(ILGenerator il) + { + return _load is { } load ? load(il, _data) : throw new InvalidOperationException("Value cannot be loaded."); + } + + public void Store(ILGenerator il) + { + if (_store is not { } store) throw new InvalidOperationException("Value cannot be stored."); + store(il, _data); + } + + public Value(Type type) + { + Type = type; + } + + public Value(int value) + { + Type = typeof(int); + _data = new OpCodeParameter { UInt = (uint)value }; + _load = (il, data) => + { + var val = (int)data.UInt; + switch (val) + { + case 0: il.Emit(OpCodes.Ldc_I4_0); break; + case 1: il.Emit(OpCodes.Ldc_I4_1); break; + case 2: il.Emit(OpCodes.Ldc_I4_2); break; + case 3: il.Emit(OpCodes.Ldc_I4_3); break; + case 4: il.Emit(OpCodes.Ldc_I4_4); break; + case 5: il.Emit(OpCodes.Ldc_I4_5); break; + case 6: il.Emit(OpCodes.Ldc_I4_6); break; + case 7: il.Emit(OpCodes.Ldc_I4_7); break; + case 8: il.Emit(OpCodes.Ldc_I4_8); break; + case -1: il.Emit(OpCodes.Ldc_I4_M1); break; + case <= 255: il.Emit(OpCodes.Ldc_I4_S, (sbyte)val); break; + default: il.Emit(OpCodes.Ldc_I4, val); break; + } + + return new Value(val); + }; + } + + public Value(string value) + { + Type = typeof(string); + _data = new OpCodeParameter { Object = value }; + _load = (il, data) => + { + var val = Unsafe.As(data.Object)!; + il.Emit(OpCodes.Ldstr, val); + return new Value(val); + }; + } + + public Value(FieldInfo field) + { + Type = field.FieldType; + _data = new OpCodeParameter { Object = field }; + _load = (il, data) => + { + var val = Unsafe.As(data.Object)!; + il.Emit(val.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, val); + return new Value(val); + }; + } + + public static Value TypeRef(Type type) + { + return new Value(typeof(Type)) + { + _data = new OpCodeParameter { Object = type }, + _load = (il, data) => + { + var val = Unsafe.As(data.Object)!; + il.Emit(OpCodes.Ldtoken, val); + return new Value(val); + }, + }; + } + + public static Value Parameter(Type type, int idx) + { + return new Value(type) + { + _data = new OpCodeParameter { UInt = (uint)idx, Object = type }, + _load = (il, data) => + { + var idx = (int)data.UInt; + var type = Unsafe.As(data.Object); + switch (idx) + { + case 0: il.Emit(OpCodes.Ldarg_0); break; + case 1: il.Emit(OpCodes.Ldarg_1); break; + case 2: il.Emit(OpCodes.Ldarg_2); break; + case 3: il.Emit(OpCodes.Ldarg_3); break; + case <= 255: il.Emit(OpCodes.Ldarg_S, (byte)idx); break; + default: il.Emit(OpCodes.Ldarg, (short)idx); break; + } + + return Value.Parameter(type, idx); + }, + _store = (il, data) => + { + var idx = (int)data.UInt; + switch (idx) + { + case <= 255: il.Emit(OpCodes.Starg_S, (byte)idx); break; + default: il.Emit(OpCodes.Starg, (short)idx); break; + } + }, + }; + } + + public static Value Local(Type type, int idx) + { + return new Value(type) + { + _data = new OpCodeParameter { UInt = (uint)idx, Object = type }, + _load = (il, data) => + { + var idx = (int)data.UInt; + var type = Unsafe.As(data.Object); + switch (idx) + { + case 0: il.Emit(OpCodes.Ldloc_0); break; + case 1: il.Emit(OpCodes.Ldloc_1); break; + case 2: il.Emit(OpCodes.Ldloc_2); break; + case 3: il.Emit(OpCodes.Ldloc_3); break; + case <= 255: il.Emit(OpCodes.Ldloc_S, (byte)idx); break; + default: il.Emit(OpCodes.Ldloc, (short)idx); break; + } + + return Local(type, idx); + }, + _store = (il, data) => + { + var idx = (int)data.UInt; + switch (idx) + { + case 0: il.Emit(OpCodes.Stloc_0); break; + case 1: il.Emit(OpCodes.Stloc_1); break; + case 2: il.Emit(OpCodes.Stloc_2); break; + case 3: il.Emit(OpCodes.Stloc_3); break; + case <= 255: il.Emit(OpCodes.Stloc_S, (byte)idx); break; + default: il.Emit(OpCodes.Stloc, (short)idx); break; + } + }, + }; + } } \ No newline at end of file diff --git a/CLI/Program.cs b/CLI/Program.cs index 5e5c866..8501dff 100755 --- a/CLI/Program.cs +++ b/CLI/Program.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.CodeDom.Compiler; +using System.Reflection; using System.Reflection.Emit; using JCIL.Java.Class; @@ -16,10 +17,10 @@ public class Program Console.WriteLine($"Load time: {(DateTime.Now - now).TotalMilliseconds}ms"); var builder = new AssemblyBuilder(new AssemblyName("Test"), false); - var dotnetClass = builder.Build(javaClass); + var dotnetClass = builder.MakeType(javaClass); try { - builder.ForceBuildMethod(javaClass.Methods[1]); + dotnetClass.GetMethod(javaClass.Methods[1]); } catch (InvalidDataException e) { @@ -35,4 +36,9 @@ public class Program assembly.Save("out.dll"); } } -} \ No newline at end of file +} + +internal static class Debug +{ + public static readonly IndentedTextWriter Writer = new(Console.Error, "| "); +} diff --git a/CLI/Types/New.cs b/CLI/Types/New.cs new file mode 100644 index 0000000..e11c080 --- /dev/null +++ b/CLI/Types/New.cs @@ -0,0 +1,116 @@ +using System.Collections.Concurrent; +using System.Collections.Frozen; +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types; + +public sealed class NewClass : TypeSurrogate +{ + public readonly Class Original; + public override TypeBuilder Type { get; } + + private readonly AssemblyBuilder _builder; + + private FrozenDictionary _fields = FrozenDictionary.Empty; + private readonly ConcurrentDictionary<(string, MethodSignature), MethodBase> _methods = []; + + public NewClass(AssemblyBuilder builder, TypeBuilder type, Class original) + { + Type = type; + _builder = builder; + Original = original; + } + + public override FieldInfo GetField(string name) + { + return _fields[name]; + } + + public override 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}`"); + + MethodAttributes attributes = default; + if ((original.AccessFlags & MethodAccessFlags.Final) != 0) attributes |= MethodAttributes.Final; + if ((original.AccessFlags & MethodAccessFlags.Static) != 0) attributes |= MethodAttributes.Static; + 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!; + if (original.Name == "") + { + return Type.DefineConstructor( + attributes, CallingConventions.Standard, + signature.ParamTypes.Select(t => _builder.MakeType(t).Type).ToArray() + ); + } + + return Type.DefineMethod( + name, attributes, CallingConventions.Standard, + _builder.MakeType(signature.ReturnType).Type, + signature.ParamTypes.Select(t => _builder.MakeType(t).Type).ToArray() + ); + }); + + if (original is null) + return method; + + if (!original.TryGetCode(out var code, out var reader)) + return method; + + if (method is MethodBuilder methodBuilder) + _builder.CompileOpCodes(Original, method, methodBuilder.GetILGenerator(), code, reader); + + if (method is ConstructorBuilder constructorBuilder) + _builder.CompileOpCodes(Original, method, constructorBuilder.GetILGenerator(), code, reader); + + return method; + } + + public override Type CallMethod(string name, MethodSignature signature, ILGenerator il) + { + var method = GetMethod(name, signature); + switch (method) + { + case MethodInfo info when method.IsVirtual: + il.Emit(OpCodes.Callvirt, info); + return info.ReturnType; + + case MethodInfo info: + il.Emit(OpCodes.Call, info); + return info.ReturnType; + + case ConstructorInfo info: + il.Emit(OpCodes.Call, info); + return typeof(void); + + default: throw new NotImplementedException(); + } + } + + internal void CreateFields() + { + var fields = new Dictionary(); + foreach (var field in Original.Fields) + { + FieldAttributes attributes = default; + if ((field.AccessFlags & FieldAccessFlags.Static) != 0) attributes |= FieldAttributes.Static; + if ((field.AccessFlags & FieldAccessFlags.Final) != 0) attributes |= FieldAttributes.InitOnly; + 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); + fields.Add(field.Name, fieldInfo); + } + + _fields = fields.ToFrozenDictionary(); + } +} \ No newline at end of file diff --git a/CLI/Types/Type.cs b/CLI/Types/Type.cs new file mode 100644 index 0000000..1d2e611 --- /dev/null +++ b/CLI/Types/Type.cs @@ -0,0 +1,18 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types; + +public abstract class TypeSurrogate +{ + 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) + { + return GetMethod(method.Name, (MethodSignature)method.Type.SpecialClassMetadata!); + } +} \ No newline at end of file diff --git a/CLI/Types/java/lang/Array.cs b/CLI/Types/java/lang/Array.cs new file mode 100644 index 0000000..f666373 --- /dev/null +++ b/CLI/Types/java/lang/Array.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.java.lang; + +public class Array(Class javaClass) : TypeSurrogate +{ + 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 override Type Type => typeof(System.Array); + public readonly TypeSurrogate ElementType = elementType; + + 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/Boolean.cs b/CLI/Types/java/lang/Boolean.cs new file mode 100644 index 0000000..31d752a --- /dev/null +++ b/CLI/Types/java/lang/Boolean.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Boolean(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..c178c6f --- /dev/null +++ b/CLI/Types/java/lang/Byte.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Byte(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..cf9441a --- /dev/null +++ b/CLI/Types/java/lang/Char.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Char(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..41d6774 --- /dev/null +++ b/CLI/Types/java/lang/Double.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Double(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..41c1114 --- /dev/null +++ b/CLI/Types/java/lang/Float.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Float(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..30d24eb --- /dev/null +++ b/CLI/Types/java/lang/Integer.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Integer(Class javaClass) : TypeSurrogate +{ + 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 new file mode 100644 index 0000000..65cad1d --- /dev/null +++ b/CLI/Types/java/lang/Long.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Reflection.Emit; +using JCIL.Java.Class; + +namespace JCIL.CLI.Types.Java.Lang; + +public sealed class Long(Class javaClass) : TypeSurrogate +{ + 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/String.cs b/CLI/Types/java/lang/String.cs new file mode 100644 index 0000000..f444e8e --- /dev/null +++ b/CLI/Types/java/lang/String.cs @@ -0,0 +1,26 @@ +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 override Type Type => typeof(string); + 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/Void.cs b/CLI/Types/java/lang/Void.cs new file mode 100644 index 0000000..b01d483 --- /dev/null +++ b/CLI/Types/java/lang/Void.cs @@ -0,0 +1,26 @@ +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 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/JavaClass/Class.cs b/JavaClass/Class.cs index f5bedb0..ef6025b 100755 --- a/JavaClass/Class.cs +++ b/JavaClass/Class.cs @@ -150,6 +150,11 @@ public sealed class Class { return Constant.Resolve(Loader, _constants, i); } + + public T GetConstant(ushort i) + { + return (T) Constant.Resolve(Loader, _constants, i)!; + } public Method? GetMethod(string name, MethodSignature signature, bool exact = false) { diff --git a/JavaClass/Method.cs b/JavaClass/Method.cs index ca02d17..ef235d5 100755 --- a/JavaClass/Method.cs +++ b/JavaClass/Method.cs @@ -308,6 +308,26 @@ public struct OpCode public OpCodeMnemonic Mnemonic; public OpCodeParameter P0; public OpCodeParameter P1; + + public string ToString(Class javaClass) + { + switch (Mnemonic) + { + case OpCodeMnemonic.GetStaticField: + { + var fieldRef = javaClass.GetConstant(P0.UShort); + return $"{Mnemonic} {fieldRef.Class}.{fieldRef.Field.Name}"; + } + + case OpCodeMnemonic.InvokeStatic: + { + var methodRef = javaClass.GetConstant(P0.UShort); + return $"{Mnemonic} {methodRef.Class}.{methodRef.Method.Name}"; + } + + default: return ToString(); + } + } public override string ToString() {