Some opcodes and casts
This commit is contained in:
+176
-69
@@ -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<Class, TypeRepresentation> _classes = [];
|
||||
private readonly Dictionary<Type, FrozenSet<Type>> _casts = [];
|
||||
private readonly Dictionary<Class, TypeRepresentation> _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));
|
||||
|
||||
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}";
|
||||
|
||||
var builder = Module.DefineType(name, flags);
|
||||
var newClass = new NewClass(this, builder, javaClass);
|
||||
|
||||
_classes[javaClass] = newClass;
|
||||
_casts[builder] = CollectTypeCasts(javaClass);
|
||||
|
||||
if (javaClass.SuperClass is {} super)
|
||||
builder.SetParent(MakeType(super).Type);
|
||||
|
||||
foreach (var javaInterface in javaClass.Interfaces)
|
||||
builder.AddInterfaceImplementation(MakeType(javaInterface).Type);
|
||||
|
||||
|
||||
newClass.CreateFields();
|
||||
return newClass;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
@@ -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,
|
||||
@@ -474,6 +490,15 @@ public sealed class AssemblyBuilder
|
||||
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:
|
||||
case OpCodeMnemonic.LMul:
|
||||
@@ -507,6 +532,7 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case OpCodeMnemonic.IntToLong:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
@@ -525,6 +551,27 @@ public sealed class AssemblyBuilder
|
||||
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:
|
||||
{
|
||||
var a = stack.Pop();
|
||||
@@ -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();
|
||||
@@ -598,6 +657,30 @@ public sealed class AssemblyBuilder
|
||||
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:
|
||||
case OpCodeMnemonic.IfICmpGt:
|
||||
@@ -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<Class>(opcode.P0.UShort));
|
||||
@@ -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<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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+15
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user