Some opcodes and casts

This commit is contained in:
Mia
2026-05-08 11:01:12 +02:00
parent 7ff1d9f481
commit 5ad527293c
3 changed files with 190 additions and 69 deletions
+174 -67
View File
@@ -1,14 +1,23 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using JCIL.CLI.Types; using JCIL.CLI.Types;
using JCIL.CLI.Types.java.lang; using JCIL.CLI.Types.java.lang;
using JCIL.CLI.Types.Java.Lang;
using JCIL.Core; using JCIL.Core;
using JCIL.Java.Class; using JCIL.Java.Class;
using Array = System.Array; 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 OpCode = JCIL.Java.Class.OpCode;
using String = JCIL.CLI.Types.Java.Lang.String;
using Void = JCIL.CLI.Types.Java.Lang.Void;
namespace JCIL.CLI; namespace JCIL.CLI;
@@ -16,7 +25,8 @@ public sealed class AssemblyBuilder
{ {
public readonly System.Reflection.Emit.AssemblyBuilder Assembly; public readonly System.Reflection.Emit.AssemblyBuilder Assembly;
public readonly ModuleBuilder Module; public readonly ModuleBuilder Module;
private readonly ConcurrentDictionary<Class, TypeRepresentation> _classes = []; private readonly Dictionary<Type, FrozenSet<Type>> _casts = [];
private readonly Dictionary<Class, TypeRepresentation> _classes = [];
public int CompiledMethods { get; private set; } public int CompiledMethods { get; private set; }
@@ -31,73 +41,77 @@ public sealed class AssemblyBuilder
public TypeRepresentation MakeType(Class javaClass) public TypeRepresentation MakeType(Class javaClass)
{ {
if (_classes.TryGetValue(javaClass, out var type)) lock (_classes)
return type;
switch (javaClass.SpecialClassMetadata)
{ {
case IntrinsicMetadata intrinsic: if (_classes.TryGetValue(javaClass, out var type))
return type;
switch (javaClass.SpecialClassMetadata)
{ {
switch (intrinsic.Type) case IntrinsicMetadata intrinsic:
{ {
case IntrinsicType.Byte: switch (intrinsic.Type)
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Byte(this, c)); {
case IntrinsicType.Char: case IntrinsicType.Byte: return _classes[javaClass] = new Byte(this, javaClass);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Char(this, c)); case IntrinsicType.Char: return _classes[javaClass] = new Char(this, javaClass);
case IntrinsicType.Int: case IntrinsicType.Int: return _classes[javaClass] = new Integer(this, javaClass);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Integer(this, c)); case IntrinsicType.Short: return _classes[javaClass] = new Short(this, javaClass);
case IntrinsicType.Short: case IntrinsicType.Long: return _classes[javaClass] = new Long(this, javaClass);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Short(this, c)); case IntrinsicType.Boolean: return _classes[javaClass] = new Boolean(this, javaClass);
case IntrinsicType.Long: case IntrinsicType.Float: return _classes[javaClass] = new Float(this, javaClass);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Long(this, c)); case IntrinsicType.Double: return _classes[javaClass] = new Double(this, javaClass);
case IntrinsicType.Boolean: case IntrinsicType.Void: return _classes[javaClass] = new Void(this, javaClass);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Boolean(this, c)); default: throw new NotImplementedException($"Unimplemented Intrinsic {intrinsic.Type}");
case IntrinsicType.Float: }
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Float(this, c)); }
case IntrinsicType.Double: case ArrayMetadata array:
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Double(this, c)); return _classes[javaClass] = new ArrayOf(this, MakeType(array.ElementClass), javaClass);
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}"); 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") lock (_casts)
{ {
if(javaClass.Name.Span is "String") TypeAttributes flags = default;
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.String(this, c)); 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") var builder = Module.DefineType(name, flags);
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Object(this, c)); var newClass = new NewClass(this, builder, javaClass);
}
_classes[javaClass] = newClass;
_casts[builder] = CollectTypeCasts(javaClass);
var newClass = (NewClass)_classes.GetOrAdd(javaClass, _ => if (javaClass.SuperClass is {} super)
{ builder.SetParent(MakeType(super).Type);
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);
foreach (var javaInterface in javaClass.Interfaces) foreach (var javaInterface in javaClass.Interfaces)
newClass.TypeBuilder.AddInterfaceImplementation(MakeType(javaInterface).Type); builder.AddInterfaceImplementation(MakeType(javaInterface).Type);
newClass.CreateFields(); newClass.CreateFields();
return newClass; return newClass;
}
}
} }
internal void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, internal void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code,
@@ -448,6 +462,7 @@ public sealed class AssemblyBuilder
case OpCodeMnemonic.IRem: case OpCodeMnemonic.IRem:
case OpCodeMnemonic.IOr: case OpCodeMnemonic.IOr:
case OpCodeMnemonic.IAnd: case OpCodeMnemonic.IAnd:
case OpCodeMnemonic.IXor:
case OpCodeMnemonic.IShl: case OpCodeMnemonic.IShl:
case OpCodeMnemonic.IShr: case OpCodeMnemonic.IShr:
case OpCodeMnemonic.IUShr: case OpCodeMnemonic.IUShr:
@@ -465,6 +480,7 @@ public sealed class AssemblyBuilder
OpCodeMnemonic.IRem => OpCodes.Rem, OpCodeMnemonic.IRem => OpCodes.Rem,
OpCodeMnemonic.IOr => OpCodes.Or, OpCodeMnemonic.IOr => OpCodes.Or,
OpCodeMnemonic.IAnd => OpCodes.And, OpCodeMnemonic.IAnd => OpCodes.And,
OpCodeMnemonic.IXor => OpCodes.Xor,
OpCodeMnemonic.IShl => OpCodes.Shl, OpCodeMnemonic.IShl => OpCodes.Shl,
OpCodeMnemonic.IShr => OpCodes.Shr, OpCodeMnemonic.IShr => OpCodes.Shr,
OpCodeMnemonic.IUShr => OpCodes.Shr_Un, OpCodeMnemonic.IUShr => OpCodes.Shr_Un,
@@ -473,6 +489,15 @@ public sealed class AssemblyBuilder
stack.Push(new Value(typeof(int))); stack.Push(new Value(typeof(int)));
break; 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.LAdd:
case OpCodeMnemonic.LSub: case OpCodeMnemonic.LSub:
@@ -506,6 +531,7 @@ public sealed class AssemblyBuilder
stack.Push(new Value(typeof(long))); stack.Push(new Value(typeof(long)));
break; break;
} }
case OpCodeMnemonic.IntToLong: case OpCodeMnemonic.IntToLong:
{ {
@@ -524,6 +550,27 @@ public sealed class AssemblyBuilder
stack.Push(new Value(typeof(int))); stack.Push(new Value(typeof(int)));
break; 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: case OpCodeMnemonic.FCmpL:
{ {
@@ -587,6 +634,18 @@ public sealed class AssemblyBuilder
break; 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: case OpCodeMnemonic.IfICmpNe:
{ {
var a = stack.Pop(); var a = stack.Pop();
@@ -597,6 +656,30 @@ public sealed class AssemblyBuilder
il.Emit(OpCodes.Brfalse, JmpLabel()); il.Emit(OpCodes.Brfalse, JmpLabel());
break; 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.IfICmpEq:
case OpCodeMnemonic.IfICmpLt: case OpCodeMnemonic.IfICmpLt:
@@ -783,6 +866,13 @@ public sealed class AssemblyBuilder
break; break;
} }
case OpCodeMnemonic.AThrow:
{
stack.Pop();
il.Emit(OpCodes.Throw);
break;
}
case OpCodeMnemonic.CheckCast: case OpCodeMnemonic.CheckCast:
{ {
var type = MakeType(javaClass.GetConstant<Class>(opcode.P0.UShort)); var type = MakeType(javaClass.GetConstant<Class>(opcode.P0.UShort));
@@ -819,7 +909,7 @@ public sealed class AssemblyBuilder
il.Emit(OpCodes.Brtrue, JmpLabel()); il.Emit(OpCodes.Brtrue, JmpLabel());
break; break;
} }
default: default:
{ {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
@@ -858,7 +948,7 @@ public sealed class AssemblyBuilder
return labels; 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)) if(ReferenceEquals(type, value.Type))
return; return;
@@ -941,18 +1031,35 @@ public sealed class AssemblyBuilder
throw new InvalidOperationException($"Value of type `{type}` is not an integer."); 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 (ReferenceEquals(from, to)) return true;
if(from.BaseType is {} baseType && IsAssignableTo(baseType, to))
return true; lock (_casts)
{
foreach (var type in from.GetInterfaces()) if(_casts.TryGetValue(from, out var casts))
if(IsAssignableTo(type, to)) if(casts.Contains(to)) return true;
return true; }
return false; return false;
} }
private FrozenSet<Type> CollectTypeCasts(Class? javaClass)
{
if(javaClass is null)
return FrozenSet<Type>.Empty;
var set = new HashSet<Type>();
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 internal readonly struct Value
+1 -1
View File
@@ -34,7 +34,7 @@ public class NewClass : TypeRepresentation
internal override void SetField(string name, Value value, ILGenerator il) internal override void SetField(string name, Value value, ILGenerator il)
{ {
var field = _fields[name]; 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); il.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field);
} }
+15 -1
View File
@@ -1,7 +1,9 @@
using System.Runtime.CompilerServices; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace JCIL.Core; namespace JCIL.Core;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class ILHelpers public static class ILHelpers
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -29,4 +31,16 @@ public static class ILHelpers
if (a < b) return -1; if (a < b) return -1;
return 0; 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;
}
} }