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