From 5ad527293c3f0a2c30daf647f79bbea48b9b8a57 Mon Sep 17 00:00:00 2001 From: Mia Date: Fri, 8 May 2026 11:01:12 +0200 Subject: [PATCH] Some opcodes and casts --- CLI/AssemblyBuilder.cs | 241 +++++++++++++++++++++++++++++------------ CLI/Types/New.cs | 2 +- JCIL.Core/ILHelpers.cs | 16 ++- 3 files changed, 190 insertions(+), 69 deletions(-) diff --git a/CLI/AssemblyBuilder.cs b/CLI/AssemblyBuilder.cs index fd92f5d..df3f270 100755 --- a/CLI/AssemblyBuilder.cs +++ b/CLI/AssemblyBuilder.cs @@ -1,14 +1,23 @@ using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using JCIL.CLI.Types; using JCIL.CLI.Types.java.lang; +using JCIL.CLI.Types.Java.Lang; using JCIL.Core; using JCIL.Java.Class; using Array = System.Array; +using Boolean = JCIL.CLI.Types.Java.Lang.Boolean; +using Byte = JCIL.CLI.Types.Java.Lang.Byte; +using Char = JCIL.CLI.Types.Java.Lang.Char; +using Double = JCIL.CLI.Types.Java.Lang.Double; +using Object = JCIL.CLI.Types.Java.Lang.Object; using OpCode = JCIL.Java.Class.OpCode; +using String = JCIL.CLI.Types.Java.Lang.String; +using Void = JCIL.CLI.Types.Java.Lang.Void; namespace JCIL.CLI; @@ -16,7 +25,8 @@ public sealed class AssemblyBuilder { public readonly System.Reflection.Emit.AssemblyBuilder Assembly; public readonly ModuleBuilder Module; - private readonly ConcurrentDictionary _classes = []; + private readonly Dictionary> _casts = []; + private readonly Dictionary _classes = []; public int CompiledMethods { get; private set; } @@ -31,73 +41,77 @@ public sealed class AssemblyBuilder public TypeRepresentation MakeType(Class javaClass) { - if (_classes.TryGetValue(javaClass, out var type)) - return type; - - switch (javaClass.SpecialClassMetadata) + lock (_classes) { - case IntrinsicMetadata intrinsic: + if (_classes.TryGetValue(javaClass, out var type)) + return type; + + switch (javaClass.SpecialClassMetadata) { - switch (intrinsic.Type) + case IntrinsicMetadata intrinsic: { - case IntrinsicType.Byte: - 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(this, c)); - case IntrinsicType.Int: - 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(this, c)); - case IntrinsicType.Long: - 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(this, c)); - case IntrinsicType.Float: - 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(this, c)); - case IntrinsicType.Void: - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Void(this, c)); - default: throw new NotImplementedException($"Unimplemented Intrinsic {intrinsic.Type}"); + switch (intrinsic.Type) + { + case IntrinsicType.Byte: return _classes[javaClass] = new Byte(this, javaClass); + case IntrinsicType.Char: return _classes[javaClass] = new Char(this, javaClass); + case IntrinsicType.Int: return _classes[javaClass] = new Integer(this, javaClass); + case IntrinsicType.Short: return _classes[javaClass] = new Short(this, javaClass); + case IntrinsicType.Long: return _classes[javaClass] = new Long(this, javaClass); + case IntrinsicType.Boolean: return _classes[javaClass] = new Boolean(this, javaClass); + case IntrinsicType.Float: return _classes[javaClass] = new Float(this, javaClass); + case IntrinsicType.Double: return _classes[javaClass] = new Double(this, javaClass); + case IntrinsicType.Void: return _classes[javaClass] = new Void(this, javaClass); + default: throw new NotImplementedException($"Unimplemented Intrinsic {intrinsic.Type}"); + } + } + case ArrayMetadata array: + return _classes[javaClass] = new ArrayOf(this, MakeType(array.ElementClass), javaClass); + } + + if (javaClass.Namespace.Span is "java/lang") + { + + if (javaClass.Name.Span is "String") + type = _classes[javaClass] = new String(this, javaClass); + + if (javaClass.Name.Span is "Object") + type = _classes[javaClass] = new Object(this, javaClass); + + if (type is not null) + { + _casts[type.Type] = CollectTypeCasts(javaClass); + return type; } } - case ArrayMetadata array: - 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)); + lock (_casts) + { + 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.IsEmpty + ? javaClass.Name.ToString() + : $"{javaClass.Namespace.ToString().Replace('/', '.')}.{javaClass.Name}"; - if(javaClass.Name.Span is "Object") - return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Object(this, c)); - } + var builder = Module.DefineType(name, flags); + var newClass = new NewClass(this, builder, javaClass); + + _classes[javaClass] = newClass; + _casts[builder] = CollectTypeCasts(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.IsEmpty - ? javaClass.Name.ToString() - : $"{javaClass.Namespace.ToString().Replace('/', '.')}.{javaClass.Name}"; - - var builder = Module.DefineType(name, flags); - - return new NewClass(this, builder, javaClass); - }); - - if (javaClass.SuperClass is {} super) - newClass.TypeBuilder.SetParent(MakeType(super).Type); + if (javaClass.SuperClass is {} super) + builder.SetParent(MakeType(super).Type); - foreach (var javaInterface in javaClass.Interfaces) - newClass.TypeBuilder.AddInterfaceImplementation(MakeType(javaInterface).Type); + foreach (var javaInterface in javaClass.Interfaces) + builder.AddInterfaceImplementation(MakeType(javaInterface).Type); + - newClass.CreateFields(); - return newClass; + newClass.CreateFields(); + return newClass; + } + } } internal void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, @@ -448,6 +462,7 @@ public sealed class AssemblyBuilder case OpCodeMnemonic.IRem: case OpCodeMnemonic.IOr: case OpCodeMnemonic.IAnd: + case OpCodeMnemonic.IXor: case OpCodeMnemonic.IShl: case OpCodeMnemonic.IShr: case OpCodeMnemonic.IUShr: @@ -465,6 +480,7 @@ public sealed class AssemblyBuilder OpCodeMnemonic.IRem => OpCodes.Rem, OpCodeMnemonic.IOr => OpCodes.Or, OpCodeMnemonic.IAnd => OpCodes.And, + OpCodeMnemonic.IXor => OpCodes.Xor, OpCodeMnemonic.IShl => OpCodes.Shl, OpCodeMnemonic.IShr => OpCodes.Shr, OpCodeMnemonic.IUShr => OpCodes.Shr_Un, @@ -473,6 +489,15 @@ public sealed class AssemblyBuilder stack.Push(new Value(typeof(int))); break; } + + case OpCodeMnemonic.INeg: + { + var v = stack.Pop(); + AssertIntCompatible(v.Type); + il.Emit(OpCodes.Neg); + stack.Push(new Value(typeof(int))); + break; + } case OpCodeMnemonic.LAdd: case OpCodeMnemonic.LSub: @@ -506,6 +531,7 @@ public sealed class AssemblyBuilder stack.Push(new Value(typeof(long))); break; } + case OpCodeMnemonic.IntToLong: { @@ -524,6 +550,27 @@ public sealed class AssemblyBuilder stack.Push(new Value(typeof(int))); break; } + + case OpCodeMnemonic.IntToChar: + { + var value = stack.Pop(); + AssertIntCompatible(value.Type); + il.Emit(OpCodes.Conv_I2); + stack.Push(new Value(typeof(char))); + break; + } + + case OpCodeMnemonic.LCmp: + { + var a = stack.Pop(); + var b = stack.Pop(); + var m = ILHelpers.LCmp; + if (a.Type != typeof(long)) throw new InvalidOperationException($"Expected long, found `{a.Type}`"); + if (b.Type != typeof(long)) throw new InvalidOperationException($"Expected long, found `{b.Type}`"); + il.Emit(OpCodes.Call, m.Method); + stack.Push(new Value(typeof(int))); + break; + } case OpCodeMnemonic.FCmpL: { @@ -587,6 +634,18 @@ public sealed class AssemblyBuilder break; } + case OpCodeMnemonic.IfLe: + { + var value = stack.Pop(); + AssertLongCompatible(value.Type); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Clt); + il.Emit(OpCodes.Brtrue, JmpLabel()); + il.Emit(OpCodes.Brfalse, JmpLabel()); + break; + } + case OpCodeMnemonic.IfICmpNe: { var a = stack.Pop(); @@ -597,6 +656,30 @@ public sealed class AssemblyBuilder il.Emit(OpCodes.Brfalse, JmpLabel()); break; } + + case OpCodeMnemonic.IfICmpGe: + { + var a = stack.Pop(); + var b = stack.Pop(); + var m = ILHelpers.ICmpGe; + AssertCast(a, typeof(int), il); + AssertCast(b, typeof(int), il); + il.Emit(OpCodes.Call, m.Method); + il.Emit(OpCodes.Brtrue, JmpLabel()); + break; + } + + case OpCodeMnemonic.IfICmpLe: + { + var a = stack.Pop(); + var b = stack.Pop(); + var m = ILHelpers.ICmpLe; + AssertCast(a, typeof(int), il); + AssertCast(b, typeof(int), il); + il.Emit(OpCodes.Call, m.Method); + il.Emit(OpCodes.Brtrue, JmpLabel()); + break; + } case OpCodeMnemonic.IfICmpEq: case OpCodeMnemonic.IfICmpLt: @@ -783,6 +866,13 @@ public sealed class AssemblyBuilder break; } + case OpCodeMnemonic.AThrow: + { + stack.Pop(); + il.Emit(OpCodes.Throw); + break; + } + case OpCodeMnemonic.CheckCast: { var type = MakeType(javaClass.GetConstant(opcode.P0.UShort)); @@ -819,7 +909,7 @@ public sealed class AssemblyBuilder il.Emit(OpCodes.Brtrue, JmpLabel()); break; } - + default: { Console.ForegroundColor = ConsoleColor.Red; @@ -858,7 +948,7 @@ public sealed class AssemblyBuilder return labels; } - internal static void AssertCast(Value value, Type type, ILGenerator il) + internal void AssertCast(Value value, Type type, ILGenerator il) { if(ReferenceEquals(type, value.Type)) return; @@ -941,18 +1031,35 @@ public sealed class AssemblyBuilder throw new InvalidOperationException($"Value of type `{type}` is not an integer."); } - private static bool IsAssignableTo(Type from, Type to) + private bool IsAssignableTo(Type from, Type to) { 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; + + lock (_casts) + { + if(_casts.TryGetValue(from, out var casts)) + if(casts.Contains(to)) return true; + } return false; } + + private FrozenSet CollectTypeCasts(Class? javaClass) + { + if(javaClass is null) + return FrozenSet.Empty; + + var set = new HashSet(); + while (javaClass != null) + { + set.Add(MakeType(javaClass).Type); + foreach (var javaInterface in javaClass.Interfaces) + set.Add(MakeType(javaInterface).Type); + javaClass = javaClass.SuperClass; + } + + return set.ToFrozenSet(); + } } internal readonly struct Value diff --git a/CLI/Types/New.cs b/CLI/Types/New.cs index 59ef612..ee6667d 100644 --- a/CLI/Types/New.cs +++ b/CLI/Types/New.cs @@ -34,7 +34,7 @@ public class NewClass : TypeRepresentation internal override void SetField(string name, Value value, ILGenerator il) { var field = _fields[name]; - AssemblyBuilder.AssertCast(value, field.FieldType, il); + Builder.AssertCast(value, field.FieldType, il); il.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field); } diff --git a/JCIL.Core/ILHelpers.cs b/JCIL.Core/ILHelpers.cs index 4ef716a..b916625 100755 --- a/JCIL.Core/ILHelpers.cs +++ b/JCIL.Core/ILHelpers.cs @@ -1,7 +1,9 @@ -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace JCIL.Core; +[SuppressMessage("ReSharper", "InconsistentNaming")] public static class ILHelpers { [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -29,4 +31,16 @@ public static class ILHelpers if (a < b) return -1; return 0; } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static bool ICmpGe(int a, int b) + { + return a >= b; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static bool ICmpLe(int a, int b) + { + return a <= b; + } } \ No newline at end of file