789 lines
27 KiB
C#
Executable File
789 lines
27 KiB
C#
Executable File
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.CompilerServices;
|
|
using JCIL.Core;
|
|
using JCIL.Java.Class;
|
|
|
|
namespace JCIL.CLI;
|
|
|
|
public sealed class AssemblyBuilder
|
|
{
|
|
public readonly System.Reflection.Emit.AssemblyBuilder Assembly;
|
|
private readonly ModuleBuilder _module;
|
|
private readonly ConcurrentDictionary<Class, Type> _classes = [];
|
|
private readonly ConcurrentDictionary<Field, FieldInfo> _fields = [];
|
|
private readonly ConcurrentDictionary<Method, LazyMethod> _methods = [];
|
|
private readonly MethodInfo _getType;
|
|
|
|
public int CompiledMethods { get; private set; }
|
|
public int TotalMethods => _methods.Count;
|
|
|
|
public AssemblyBuilder(AssemblyName name, bool runnable)
|
|
{
|
|
Assembly = runnable
|
|
? System.Reflection.Emit.AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect)
|
|
: new PersistedAssemblyBuilder(name, typeof(int).Assembly);
|
|
|
|
_module = Assembly.DefineDynamicModule("JCIL_Output");
|
|
_getType = typeof(object).GetMethod(nameof(object.GetType), BindingFlags.Instance | BindingFlags.Public)!;
|
|
}
|
|
|
|
public void ForceBuildMethod(Method method)
|
|
{
|
|
_ = _methods[method].Method;
|
|
}
|
|
|
|
public Type Build(Class javaClass)
|
|
{
|
|
switch (javaClass.SpecialClassMetadata)
|
|
{
|
|
case IntrinsicMetadata { Type: IntrinsicType.Boolean }: return typeof(bool);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Byte }: return typeof(byte);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Char }: return typeof(char);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Short }: return typeof(short);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Int }: return typeof(int);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Long }: return typeof(long);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Float }: return typeof(float);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Double }: return typeof(double);
|
|
case IntrinsicMetadata { Type: IntrinsicType.Void }: return typeof(void);
|
|
case ArrayMetadata { ElementClass: var elementClass }: return Build(elementClass).MakeArrayType();
|
|
}
|
|
|
|
var build = false;
|
|
var theType = _classes.GetOrAdd(javaClass, _ =>
|
|
{
|
|
TypeAttributes flags = default;
|
|
if ((javaClass.AccessFlags & ClassAccessFlags.Public) != 0) flags |= TypeAttributes.Public;
|
|
if ((javaClass.AccessFlags & ClassAccessFlags.Abstract) != 0) flags |= TypeAttributes.Abstract;
|
|
if ((javaClass.AccessFlags & ClassAccessFlags.Final) != 0) flags |= TypeAttributes.Sealed;
|
|
if ((javaClass.AccessFlags & ClassAccessFlags.Interface) != 0) flags |= TypeAttributes.Interface;
|
|
|
|
var name = javaClass.Namespace.Length == 0
|
|
? javaClass.Name.ToString()
|
|
: $"{javaClass.Namespace.ToString().Replace('/', '.')}.{javaClass.Name}";
|
|
|
|
build = true;
|
|
return _module.DefineType(name, flags);
|
|
});
|
|
|
|
if (build && theType is TypeBuilder typeBuilder)
|
|
{
|
|
// Console.WriteLine($"Building type `{typeBuilder.FullName}`");
|
|
|
|
if (!theType.IsInterface && javaClass.SuperClass is not null && javaClass.SuperClass != javaClass)
|
|
typeBuilder.SetParent(Build(javaClass.SuperClass));
|
|
|
|
foreach (var field in javaClass.Fields)
|
|
{
|
|
FieldAttributes flags = default;
|
|
if ((field.AccessFlags & FieldAccessFlags.Static) != 0) flags |= FieldAttributes.Static;
|
|
if ((field.AccessFlags & FieldAccessFlags.Final) != 0) flags |= FieldAttributes.InitOnly;
|
|
if ((field.AccessFlags & FieldAccessFlags.Public) != 0) flags |= FieldAttributes.Public;
|
|
if ((field.AccessFlags & FieldAccessFlags.Private) != 0) flags |= FieldAttributes.Private;
|
|
if ((field.AccessFlags & FieldAccessFlags.Protected) != 0) flags |= FieldAttributes.Family;
|
|
|
|
var type = Build(field.Type);
|
|
_fields.TryAdd(field, typeBuilder.DefineField(field.Name, type, flags));
|
|
}
|
|
|
|
foreach (var method in javaClass.Methods)
|
|
{
|
|
MethodAttributes flags = default;
|
|
if ((method.AccessFlags & MethodAccessFlags.Static) != 0) flags |= MethodAttributes.Static;
|
|
else flags |= MethodAttributes.Virtual;
|
|
if ((method.AccessFlags & MethodAccessFlags.Public) != 0) flags |= MethodAttributes.Public;
|
|
if ((method.AccessFlags & MethodAccessFlags.Private) != 0) flags |= MethodAttributes.Private;
|
|
if ((method.AccessFlags & MethodAccessFlags.Protected) != 0) flags |= MethodAttributes.Family;
|
|
if ((method.AccessFlags & MethodAccessFlags.Abstract) != 0) flags |= MethodAttributes.Abstract;
|
|
if ((method.AccessFlags & MethodAccessFlags.Final) != 0) flags |= MethodAttributes.Final;
|
|
|
|
var signature = (MethodSignature)method.Type.SpecialClassMetadata!;
|
|
var paramTypes = signature.ParamTypes.Select(Build).ToArray();
|
|
if (method.Name == "<init>")
|
|
{
|
|
flags &= ~MethodAttributes.Virtual;
|
|
var ctor = typeBuilder.DefineConstructor(flags, CallingConventions.Standard, paramTypes);
|
|
if (method.Attributes.OfType<MethodParameters>().FirstOrDefault() is { } attrib)
|
|
{
|
|
for (var j = 0; j < attrib.Parameters.Count; j++)
|
|
{
|
|
// TODO populate parameter attributes
|
|
ctor.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1);
|
|
}
|
|
}
|
|
|
|
_methods.TryAdd(method, new LazyMethod(ctor, () =>
|
|
{
|
|
Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`");
|
|
if (method.TryGetCode(out var code, out var opcodes))
|
|
CompileOpCodes(javaClass, ctor, ctor.GetILGenerator(), code, opcodes);
|
|
}));
|
|
}
|
|
else if (method.Name == "<clinit>")
|
|
{
|
|
var ctor = typeBuilder.DefineTypeInitializer();
|
|
if (method.Attributes.OfType<MethodParameters>().FirstOrDefault() is { } attrib)
|
|
{
|
|
for (var j = 0; j < attrib.Parameters.Count; j++)
|
|
{
|
|
// TODO populate parameter attributes
|
|
ctor.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1);
|
|
}
|
|
}
|
|
|
|
_methods.TryAdd(method, new LazyMethod(ctor, () =>
|
|
{
|
|
Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`");
|
|
if (method.TryGetCode(out var code, out var opcodes))
|
|
CompileOpCodes(javaClass, ctor, ctor.GetILGenerator(), code, opcodes);
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
var methodBuilder = typeBuilder.DefineMethod(method.Name, flags);
|
|
methodBuilder.SetReturnType(Build(signature.ReturnType));
|
|
methodBuilder.SetParameters(paramTypes);
|
|
|
|
if (method.Attributes.OfType<MethodParameters>().FirstOrDefault() is { } attrib)
|
|
{
|
|
for (var j = 0; j < attrib.Parameters.Count; j++)
|
|
{
|
|
// TODO populate parameter attributes
|
|
methodBuilder.DefineParameter(j, ParameterAttributes.None, attrib.Parameters[j].Item1);
|
|
}
|
|
}
|
|
|
|
_methods.TryAdd(method, new LazyMethod(methodBuilder, () =>
|
|
{
|
|
Console.WriteLine($"Building method `{typeBuilder.FullName}.{method.Name}`");
|
|
if (method.TryGetCode(out var code, out var opcodes))
|
|
CompileOpCodes(javaClass, methodBuilder, methodBuilder.GetILGenerator(), code, opcodes);
|
|
}));
|
|
}
|
|
}
|
|
|
|
return typeBuilder.CreateType();
|
|
}
|
|
|
|
foreach (var interfaceClass in javaClass.Interfaces)
|
|
_ = Build(interfaceClass);
|
|
|
|
return theType;
|
|
}
|
|
|
|
private void CompileOpCodes(Class javaClass, MethodBase methodBase, ILGenerator il, Code code, OpCodeReader opcodes)
|
|
{
|
|
var locals = new (Type, int)[code.MaxLocals];
|
|
var localsCount = 0;
|
|
var localsIdx = 0;
|
|
|
|
if (!methodBase.IsStatic)
|
|
{
|
|
il.DeclareLocal(methodBase.DeclaringType!);
|
|
il.Emit(OpCodes.Ldarg_0);
|
|
il.Emit(OpCodes.Stloc_0);
|
|
locals[localsIdx++] = (methodBase.DeclaringType!, localsCount++);
|
|
}
|
|
|
|
var parameters = methodBase.GetParameters();
|
|
for (int i = 0, j = methodBase.IsStatic ? 0 : 1; i < parameters.Length; i++, j++)
|
|
{
|
|
il.DeclareLocal(parameters[i].ParameterType);
|
|
il.Emit(OpCodes.Ldarg, (short) j);
|
|
il.Emit(OpCodes.Stloc, (short) j);
|
|
locals[localsIdx++] = (parameters[i].ParameterType, localsCount++);
|
|
}
|
|
|
|
if (code.Attributes.OfType<StackMapTable>().FirstOrDefault() is not { Frames: var frames })
|
|
frames = [];
|
|
|
|
var position = opcodes.Reader.Stream.Position;
|
|
var labels = new Dictionary<long, Label>();
|
|
while (opcodes.MoveNext())
|
|
{
|
|
labels.Add(position, il.DefineLabel());
|
|
position = opcodes.Reader.Stream.Position;
|
|
}
|
|
|
|
opcodes.Reset();
|
|
position = opcodes.Reader.Stream.Position;
|
|
|
|
void AddLocal(in StackMapTable.VerificationTypeInfo info)
|
|
{
|
|
Type compiledType;
|
|
switch (info.Tag)
|
|
{
|
|
case StackMapTable.VerificationTypeInfoTag.Top:
|
|
locals[localsIdx++] = default;
|
|
return;
|
|
|
|
case StackMapTable.VerificationTypeInfoTag.Integer: compiledType = typeof(int); break;
|
|
case StackMapTable.VerificationTypeInfoTag.Long: compiledType = typeof(long); break;
|
|
case StackMapTable.VerificationTypeInfoTag.Float: compiledType = typeof(float); break;
|
|
case StackMapTable.VerificationTypeInfoTag.Double: compiledType = typeof(double); break;
|
|
|
|
case StackMapTable.VerificationTypeInfoTag.Object:
|
|
{
|
|
var type = (Class)javaClass.GetConstant(info.Parameter)!;
|
|
compiledType = Build(type);
|
|
break;
|
|
}
|
|
|
|
case StackMapTable.VerificationTypeInfoTag.UninitializedThis:
|
|
compiledType = methodBase.DeclaringType!;
|
|
break;
|
|
|
|
default: throw new NotImplementedException($"Unimplemented local tag {info.Tag}");
|
|
}
|
|
|
|
locals[localsIdx++] = (compiledType, localsCount++);
|
|
il.DeclareLocal(compiledType);
|
|
}
|
|
|
|
for (var f = -1; opcodes.MoveNext();)
|
|
{
|
|
if (f + 1 < frames.Count && position >= frames[f + 1].StartOffset)
|
|
{
|
|
switch (frames[++f])
|
|
{
|
|
case StackMapTable.SameFrame: break;
|
|
case StackMapTable.SameFrameExtended: break;
|
|
case StackMapTable.OneLocalFrame: break;
|
|
case StackMapTable.OneLocalFrameExtended: break;
|
|
|
|
case StackMapTable.AppendFrame frame:
|
|
foreach (var info in frame.Locals) AddLocal(info);
|
|
break;
|
|
|
|
case StackMapTable.ChopFrame frame:
|
|
localsIdx -= frame.Count;
|
|
break;
|
|
|
|
case StackMapTable.FullFrame frame:
|
|
{
|
|
localsIdx = 0;
|
|
foreach (var info in frame.Locals) AddLocal(info);
|
|
break;
|
|
}
|
|
|
|
default: throw new NotImplementedException($"Unimplemented {frames[f]}");
|
|
}
|
|
}
|
|
|
|
il.MarkLabel(labels[position]);
|
|
var opcode = opcodes.Current;
|
|
|
|
Label JmpLabel()
|
|
{
|
|
return labels[position + (short)opcode.P0.UShort];
|
|
}
|
|
|
|
void LoadLocal(int idx)
|
|
{
|
|
var loc = locals[idx].Item2;
|
|
switch (loc)
|
|
{
|
|
case 0: il.Emit(OpCodes.Ldloc_0); break;
|
|
case 1: il.Emit(OpCodes.Ldloc_1); break;
|
|
case 2: il.Emit(OpCodes.Ldloc_2); break;
|
|
case 3: il.Emit(OpCodes.Ldloc_3); break;
|
|
case <= 255: il.Emit(OpCodes.Ldloc_S, (byte)loc); break;
|
|
default: il.Emit(OpCodes.Ldloc, (short)loc); break;
|
|
}
|
|
}
|
|
|
|
void StoreLocal(Type ty, int idx)
|
|
{
|
|
ref var loc = ref locals[idx];
|
|
if (loc.Item2 == -1 || loc.Item1 is null || (loc.Item1 != ty && (loc.Item1.IsValueType || ty.IsValueType)))
|
|
{
|
|
il.DeclareLocal(ty);
|
|
loc = (ty, localsCount++);
|
|
}
|
|
|
|
switch (loc.Item2)
|
|
{
|
|
case 0: il.Emit(OpCodes.Stloc_0); break;
|
|
case 1: il.Emit(OpCodes.Stloc_1); break;
|
|
case 2: il.Emit(OpCodes.Stloc_2); break;
|
|
case 3: il.Emit(OpCodes.Stloc_3); break;
|
|
case <= 255: il.Emit(OpCodes.Stloc_S, (byte)loc.Item2); break;
|
|
default: il.Emit(OpCodes.Stloc, (short)loc.Item2); break;
|
|
}
|
|
}
|
|
|
|
switch (opcode.Mnemonic)
|
|
{
|
|
case OpCodeMnemonic.Nop: break;
|
|
case OpCodeMnemonic.AConstNull: il.Emit(OpCodes.Ldnull); break;
|
|
case OpCodeMnemonic.IConstM1: il.Emit(OpCodes.Ldc_I4_M1); break;
|
|
case OpCodeMnemonic.IConst0: il.Emit(OpCodes.Ldc_I4_0); break;
|
|
case OpCodeMnemonic.IConst1: il.Emit(OpCodes.Ldc_I4_1); break;
|
|
case OpCodeMnemonic.IConst2: il.Emit(OpCodes.Ldc_I4_2); break;
|
|
case OpCodeMnemonic.IConst3: il.Emit(OpCodes.Ldc_I4_3); break;
|
|
case OpCodeMnemonic.IConst4: il.Emit(OpCodes.Ldc_I4_4); break;
|
|
case OpCodeMnemonic.IConst5: il.Emit(OpCodes.Ldc_I4_5); break;
|
|
case OpCodeMnemonic.LConst0: il.Emit(OpCodes.Ldc_I8, 0L); break;
|
|
case OpCodeMnemonic.LConst1: il.Emit(OpCodes.Ldc_I8, 1L); break;
|
|
case OpCodeMnemonic.FConst0: il.Emit(OpCodes.Ldc_R4, 0f); break;
|
|
case OpCodeMnemonic.FConst1: il.Emit(OpCodes.Ldc_R4, 1f); break;
|
|
case OpCodeMnemonic.FConst2: il.Emit(OpCodes.Ldc_R4, 2f); break;
|
|
case OpCodeMnemonic.DConst0: il.Emit(OpCodes.Ldc_R4, 0d); break;
|
|
case OpCodeMnemonic.DConst1: il.Emit(OpCodes.Ldc_R4, 1d); break;
|
|
|
|
case OpCodeMnemonic.PushByte: il.Emit(OpCodes.Ldc_I4, (int)opcode.P0.Byte); break;
|
|
case OpCodeMnemonic.PushShort: il.Emit(OpCodes.Ldc_I4, (int)(short)opcode.P0.UShort); break;
|
|
|
|
case OpCodeMnemonic.Ldc:
|
|
case OpCodeMnemonic.LdcW:
|
|
case OpCodeMnemonic.Ldc2W:
|
|
{
|
|
var constant = javaClass.GetConstant(opcode.P0.UShort);
|
|
switch (constant)
|
|
{
|
|
//TODO this is wrong
|
|
case Class c: il.Emit(OpCodes.Ldtoken, Build(c)); break;
|
|
|
|
case int val: il.Emit(OpCodes.Ldc_I4, val); break;
|
|
case short val: il.Emit(OpCodes.Ldc_I4, (int) val); break;
|
|
case long val: il.Emit(OpCodes.Ldc_I8, val); break;
|
|
case float val: il.Emit(OpCodes.Ldc_R4, val); break;
|
|
case double val: il.Emit(OpCodes.Ldc_R8, val); break;
|
|
|
|
case string str: il.Emit(OpCodes.Ldstr, str); break;
|
|
default:
|
|
throw new NotImplementedException(
|
|
$"Unimplemented ldc {constant?.GetType().FullName ?? "null"}");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.ILoad:
|
|
case OpCodeMnemonic.LLoad:
|
|
case OpCodeMnemonic.ALoad: LoadLocal(opcode.P0.Byte); break;
|
|
|
|
case OpCodeMnemonic.ILoad0:
|
|
case OpCodeMnemonic.LLoad0:
|
|
case OpCodeMnemonic.FLoad0:
|
|
case OpCodeMnemonic.DLoad0:
|
|
case OpCodeMnemonic.ALoad0: LoadLocal(0); break;
|
|
case OpCodeMnemonic.ALoad1:
|
|
case OpCodeMnemonic.LLoad1:
|
|
case OpCodeMnemonic.FLoad1:
|
|
case OpCodeMnemonic.DLoad1:
|
|
case OpCodeMnemonic.ILoad1: LoadLocal(1); break;
|
|
case OpCodeMnemonic.ALoad2:
|
|
case OpCodeMnemonic.LLoad2:
|
|
case OpCodeMnemonic.FLoad2:
|
|
case OpCodeMnemonic.DLoad2:
|
|
case OpCodeMnemonic.ILoad2: LoadLocal(2); break;
|
|
case OpCodeMnemonic.ALoad3:
|
|
case OpCodeMnemonic.LLoad3:
|
|
case OpCodeMnemonic.FLoad3:
|
|
case OpCodeMnemonic.DLoad3:
|
|
case OpCodeMnemonic.ILoad3: LoadLocal(3); break;
|
|
|
|
case OpCodeMnemonic.IALoad: il.Emit(OpCodes.Ldelem_I4); break;
|
|
case OpCodeMnemonic.LALoad: il.Emit(OpCodes.Ldelem_I8); break;
|
|
case OpCodeMnemonic.AALoad: il.Emit(OpCodes.Ldelem_Ref); break;
|
|
case OpCodeMnemonic.BALoad: il.Emit(OpCodes.Ldelem_I1); break;
|
|
case OpCodeMnemonic.CALoad: il.Emit(OpCodes.Ldelem_I2); break;
|
|
case OpCodeMnemonic.SALoad: il.Emit(OpCodes.Ldelem_I2); break;
|
|
|
|
case OpCodeMnemonic.AStore: StoreLocal(typeof(object), opcode.P0.Byte); break;
|
|
case OpCodeMnemonic.IStore: StoreLocal(typeof(int), opcode.P0.Byte); break;
|
|
case OpCodeMnemonic.LStore: StoreLocal(typeof(long), opcode.P0.Byte); break;
|
|
case OpCodeMnemonic.FStore: StoreLocal(typeof(float), opcode.P0.Byte); break;
|
|
case OpCodeMnemonic.DStore: StoreLocal(typeof(double), opcode.P0.Byte); break;
|
|
|
|
case OpCodeMnemonic.AStore0: StoreLocal(typeof(object), 0); break;
|
|
case OpCodeMnemonic.IStore0: StoreLocal(typeof(int), 0); break;
|
|
case OpCodeMnemonic.LStore0: StoreLocal(typeof(long), 0); break;
|
|
case OpCodeMnemonic.AStore1:StoreLocal(typeof(object), 1); break;
|
|
case OpCodeMnemonic.IStore1:StoreLocal(typeof(int), 1); break;
|
|
case OpCodeMnemonic.LStore1:StoreLocal(typeof(long), 1); break;
|
|
case OpCodeMnemonic.AStore2:StoreLocal(typeof(object), 2); break;
|
|
case OpCodeMnemonic.IStore2:StoreLocal(typeof(int), 2); break;
|
|
case OpCodeMnemonic.LStore2:StoreLocal(typeof(long), 2); break;
|
|
case OpCodeMnemonic.AStore3:StoreLocal(typeof(object), 3); break;
|
|
case OpCodeMnemonic.IStore3:StoreLocal(typeof(int), 3); break;
|
|
case OpCodeMnemonic.LStore3:StoreLocal(typeof(long), 3); break;
|
|
|
|
case OpCodeMnemonic.LAStore: il.Emit(OpCodes.Stelem_I8); break;
|
|
case OpCodeMnemonic.AAStore: il.Emit(OpCodes.Stelem_Ref); break;
|
|
case OpCodeMnemonic.BAStore: il.Emit(OpCodes.Stelem_I1); break;
|
|
case OpCodeMnemonic.CAStore: il.Emit(OpCodes.Stelem_I2); break;
|
|
|
|
|
|
case OpCodeMnemonic.Pop: il.Emit(OpCodes.Pop); break;
|
|
case OpCodeMnemonic.Pop2:
|
|
{
|
|
var class1Label = il.DefineLabel();
|
|
var class2Label = il.DefineLabel();
|
|
var continueLabel = il.DefineLabel();
|
|
il.Emit(OpCodes.Dup);
|
|
il.EmitCall(OpCodes.Call, _getType, null);
|
|
il.Emit(OpCodes.Dup);
|
|
il.Emit(OpCodes.Ldtoken, typeof(long).MetadataToken);
|
|
il.Emit(OpCodes.Pop);
|
|
il.Emit(OpCodes.Beq, class2Label);
|
|
il.Emit(OpCodes.Ldtoken, typeof(double).MetadataToken);
|
|
il.Emit(OpCodes.Beq, class2Label);
|
|
il.MarkLabel(class1Label);
|
|
il.Emit(OpCodes.Pop);
|
|
il.Emit(OpCodes.Pop);
|
|
il.Emit(OpCodes.Br, continueLabel);
|
|
il.MarkLabel(class2Label);
|
|
il.Emit(OpCodes.Pop);
|
|
il.Emit(OpCodes.Br, continueLabel);
|
|
il.MarkLabel(continueLabel);
|
|
break;
|
|
}
|
|
case OpCodeMnemonic.Dup: il.Emit(OpCodes.Dup); break;
|
|
|
|
case OpCodeMnemonic.IAdd:
|
|
case OpCodeMnemonic.LAdd:
|
|
case OpCodeMnemonic.FAdd:
|
|
case OpCodeMnemonic.DAdd: il.Emit(OpCodes.Add); break;
|
|
case OpCodeMnemonic.ISub:
|
|
case OpCodeMnemonic.LSub:
|
|
case OpCodeMnemonic.FSub:
|
|
case OpCodeMnemonic.DSub: il.Emit(OpCodes.Sub); break;
|
|
case OpCodeMnemonic.IMul:
|
|
case OpCodeMnemonic.LMul:
|
|
case OpCodeMnemonic.FMul:
|
|
case OpCodeMnemonic.DMul: il.Emit(OpCodes.Mul); break;
|
|
case OpCodeMnemonic.IDiv:
|
|
case OpCodeMnemonic.LDiv:
|
|
case OpCodeMnemonic.FDiv:
|
|
case OpCodeMnemonic.DDiv: il.Emit(OpCodes.Div); break;
|
|
case OpCodeMnemonic.IRem:
|
|
case OpCodeMnemonic.LRem:
|
|
case OpCodeMnemonic.FRem:
|
|
case OpCodeMnemonic.DRem: il.Emit(OpCodes.Rem); break;
|
|
case OpCodeMnemonic.INeg:
|
|
case OpCodeMnemonic.LNeg: il.Emit(OpCodes.Neg); break;
|
|
|
|
case OpCodeMnemonic.IShl:
|
|
case OpCodeMnemonic.LShl: il.Emit(OpCodes.Shl); break;
|
|
case OpCodeMnemonic.IShr:
|
|
case OpCodeMnemonic.LShr: il.Emit(OpCodes.Shr); break;
|
|
case OpCodeMnemonic.IUShr:
|
|
case OpCodeMnemonic.LUShr: il.Emit(OpCodes.Shr_Un); break;
|
|
case OpCodeMnemonic.IAnd:
|
|
case OpCodeMnemonic.LAnd: il.Emit(OpCodes.And); break;
|
|
case OpCodeMnemonic.IOr:
|
|
case OpCodeMnemonic.LOr: il.Emit(OpCodes.Or); break;
|
|
|
|
case OpCodeMnemonic.IInc:
|
|
{
|
|
LoadLocal(opcode.P0.UShort);
|
|
il.Emit(OpCodes.Ldc_I4, opcode.P0.Byte);
|
|
il.Emit(OpCodes.Add);
|
|
StoreLocal(typeof(int), opcode.P0.UShort);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.IntToLong: il.Emit(OpCodes.Conv_I8); break;
|
|
case OpCodeMnemonic.IntToByte: il.Emit(OpCodes.Conv_I1); break;
|
|
case OpCodeMnemonic.IntToChar: il.Emit(OpCodes.Conv_U2); break;
|
|
case OpCodeMnemonic.IntToShort: il.Emit(OpCodes.Conv_I2); break;
|
|
|
|
case OpCodeMnemonic.LCmp:
|
|
{
|
|
var func = ILHelpers.LCmp;
|
|
il.EmitCall(OpCodes.Call, func.Method, null);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.InvokeSpecial:
|
|
case OpCodeMnemonic.InvokeStatic:
|
|
{
|
|
var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(methodRef.Class);
|
|
|
|
var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory())
|
|
.SpecialClassMetadata!;
|
|
|
|
var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature);
|
|
if (method is null || !_methods.TryGetValue(method, out var compiledMethod))
|
|
throw new Exception($"Method {methodRef.Method.Name} not found");
|
|
|
|
if (compiledMethod.Method is MethodInfo methodInfo)
|
|
il.EmitCall(OpCodes.Call, methodInfo, null);
|
|
|
|
if (compiledMethod.Method is ConstructorInfo constructorInfo)
|
|
il.Emit(OpCodes.Call, constructorInfo);
|
|
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.InvokeVirtual:
|
|
{
|
|
var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(methodRef.Class);
|
|
|
|
var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory())
|
|
.SpecialClassMetadata!;
|
|
|
|
var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature);
|
|
if (method is null || !_methods.TryGetValue(method, out var compiledMethod))
|
|
throw new Exception($"Method {methodRef.Method.Name} not found");
|
|
|
|
if (compiledMethod.Method is MethodInfo methodInfo)
|
|
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
|
|
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.InvokeInterface:
|
|
{
|
|
var methodRef = (MethodRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(methodRef.Class);
|
|
|
|
var signature = (MethodSignature)javaClass.Loader.GetClass(methodRef.Method.Descriptor.AsMemory())
|
|
.SpecialClassMetadata!;
|
|
|
|
var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature);
|
|
if (method is null || !_methods.TryGetValue(method, out var compiledMethod))
|
|
throw new Exception($"Method {methodRef.Method.Name} not found");
|
|
|
|
if (compiledMethod.Method is MethodInfo methodInfo)
|
|
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
|
|
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.New:
|
|
{
|
|
var obj = (Class)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
var objT = Build(obj);
|
|
|
|
// TODO Make this work with any number of parameters
|
|
var posBackup = opcodes.Reader.Stream.Position;
|
|
opcodes.MoveNext();
|
|
if (opcodes.Current.Mnemonic == OpCodeMnemonic.Dup)
|
|
{
|
|
opcodes.MoveNext();
|
|
if (opcodes.Current.Mnemonic == OpCodeMnemonic.InvokeSpecial)
|
|
{
|
|
var methodRef = (MethodRef)javaClass.GetConstant(opcodes.Current.P0.UShort)!;
|
|
_ = Build(methodRef.Class);
|
|
|
|
var signature = (MethodSignature)javaClass.Loader
|
|
.GetClass(methodRef.Method.Descriptor.AsMemory())
|
|
.SpecialClassMetadata!;
|
|
|
|
var method = methodRef.Class.GetMethod(methodRef.Method.Name, signature);
|
|
if (method is null || !_methods.TryGetValue(method, out var compiledMethod))
|
|
throw new Exception($"Method {methodRef.Method.Name} not found");
|
|
|
|
if (compiledMethod.Method is ConstructorInfo constructorInfo)
|
|
{
|
|
il.Emit(OpCodes.Newobj, constructorInfo);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
opcodes.Reader.Stream.Position = posBackup;
|
|
var func = RuntimeHelpers.GetUninitializedObject;
|
|
il.Emit(OpCodes.Ldtoken, objT);
|
|
il.Emit(OpCodes.Call, func.Method);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.NewArray:
|
|
{
|
|
il.Emit(OpCodes.Newarr, opcode.P0.Byte switch
|
|
{
|
|
4 => typeof(bool),
|
|
5 => typeof(char),
|
|
6 => typeof(float),
|
|
7 => typeof(double),
|
|
8 => typeof(byte),
|
|
9 => typeof(short),
|
|
10 => typeof(int),
|
|
11 => typeof(long),
|
|
_ => throw new NotImplementedException(nameof(opcode.P0)),
|
|
});
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.ANewArray:
|
|
{
|
|
var elem = (Class)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
il.Emit(OpCodes.Newarr, Build(elem));
|
|
break;
|
|
}
|
|
|
|
|
|
case OpCodeMnemonic.IfEq: il.Emit(OpCodes.Brfalse, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfNe: il.Emit(OpCodes.Brtrue, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfLt:
|
|
il.Emit(OpCodes.Ldc_I4_0);
|
|
il.Emit(OpCodes.Blt, JmpLabel());
|
|
break;
|
|
case OpCodeMnemonic.IfLe:
|
|
il.Emit(OpCodes.Ldc_I4_0);
|
|
il.Emit(OpCodes.Ble, JmpLabel());
|
|
break;
|
|
case OpCodeMnemonic.IfGe:
|
|
il.Emit(OpCodes.Ldc_I4_0);
|
|
il.Emit(OpCodes.Bge, JmpLabel());
|
|
break;
|
|
case OpCodeMnemonic.IfGt:
|
|
il.Emit(OpCodes.Ldc_I4_0);
|
|
il.Emit(OpCodes.Bgt, JmpLabel());
|
|
break;
|
|
|
|
case OpCodeMnemonic.IfICmpEq: il.Emit(OpCodes.Beq, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfICmpNe: il.Emit(OpCodes.Bne_Un, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfICmpLt: il.Emit(OpCodes.Blt, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfICmpLe: il.Emit(OpCodes.Ble, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfICmpGe: il.Emit(OpCodes.Bge, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfICmpGt: il.Emit(OpCodes.Bgt, JmpLabel()); break;
|
|
|
|
case OpCodeMnemonic.IfACmpEq:
|
|
{
|
|
il.Emit(OpCodes.Ceq);
|
|
il.Emit(OpCodes.Brtrue, JmpLabel());
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.IfACmpNe:
|
|
{
|
|
il.Emit(OpCodes.Ceq);
|
|
il.Emit(OpCodes.Brfalse, JmpLabel());
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.Goto: il.Emit(OpCodes.Br, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfNull: il.Emit(OpCodes.Brfalse, JmpLabel()); break;
|
|
case OpCodeMnemonic.IfNonNull: il.Emit(OpCodes.Brtrue, JmpLabel()); break;
|
|
|
|
case OpCodeMnemonic.Return:
|
|
case OpCodeMnemonic.IReturn:
|
|
case OpCodeMnemonic.LReturn:
|
|
case OpCodeMnemonic.FReturn:
|
|
case OpCodeMnemonic.DReturn:
|
|
case OpCodeMnemonic.AReturn:
|
|
{
|
|
il.Emit(OpCodes.Ret);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.SetField:
|
|
{
|
|
var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(fieldRef.Class);
|
|
var field = fieldRef.Class.GetField(fieldRef.Field.Name)!;
|
|
var compiledField = _fields[field];
|
|
il.Emit(OpCodes.Stfld, compiledField);
|
|
break;
|
|
}
|
|
case OpCodeMnemonic.SetStaticField:
|
|
{
|
|
var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(fieldRef.Class);
|
|
var field = fieldRef.Class.GetField(fieldRef.Field.Name)!;
|
|
var compiledField = _fields[field];
|
|
il.Emit(OpCodes.Stsfld, compiledField);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.GetField:
|
|
{
|
|
var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(fieldRef.Class);
|
|
var field = fieldRef.Class.GetField(fieldRef.Field.Name)!;
|
|
var compiledField = _fields[field];
|
|
il.Emit(OpCodes.Ldfld, compiledField);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.GetStaticField:
|
|
{
|
|
var fieldRef = (FieldRef)javaClass.GetConstant(opcode.P0.UShort)!;
|
|
_ = Build(fieldRef.Class);
|
|
var field = fieldRef.Class.GetField(fieldRef.Field.Name)!;
|
|
var compiledField = _fields[field];
|
|
il.Emit(OpCodes.Ldsfld, compiledField);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.ArrayLen:
|
|
{
|
|
il.Emit(OpCodes.Ldlen);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.AThrow:
|
|
{
|
|
il.Emit(OpCodes.Throw);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.InstanceOf:
|
|
{
|
|
var type = Build((Class)javaClass.GetConstant(opcode.P0.UShort)!);
|
|
il.Emit(OpCodes.Isinst, type);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.CheckCast:
|
|
{
|
|
var type = Build((Class)javaClass.GetConstant(opcode.P0.UShort)!);
|
|
il.Emit(OpCodes.Castclass, type);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.MonitorEnter:
|
|
{
|
|
Action<object> func = Monitor.Enter;
|
|
il.EmitCall(OpCodes.Call, func.Method, null);
|
|
break;
|
|
}
|
|
|
|
case OpCodeMnemonic.MonitorExit:
|
|
{
|
|
Action<object> func = Monitor.Exit;
|
|
il.EmitCall(OpCodes.Call, func.Method, null);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
il.Emit(OpCodes.Nop);
|
|
Console.WriteLine($"Unknown opcode: {opcode.Mnemonic}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
position = opcodes.Reader.Stream.Position;
|
|
}
|
|
|
|
CompiledMethods += 1;
|
|
}
|
|
}
|
|
|
|
internal class LazyMethod(MethodBase method, Action compile)
|
|
{
|
|
public bool Compiled { get; private set; }
|
|
|
|
public MethodBase Method
|
|
{
|
|
get
|
|
{
|
|
if (!Compiled)
|
|
{
|
|
Compiled = true;
|
|
compile();
|
|
}
|
|
|
|
return method;
|
|
}
|
|
}
|
|
} |