Will the crap ever end?
This commit is contained in:
+321
-33
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
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.Java.Class;
|
||||
using Array = System.Array;
|
||||
using OpCode = JCIL.Java.Class.OpCode;
|
||||
|
||||
namespace JCIL.CLI;
|
||||
@@ -46,6 +48,8 @@ public sealed class AssemblyBuilder
|
||||
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Char(c));
|
||||
case IntrinsicType.Int:
|
||||
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Integer(c));
|
||||
case IntrinsicType.Short:
|
||||
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Short(c));
|
||||
case IntrinsicType.Long:
|
||||
return _classes.GetOrAdd(javaClass, c => new JCIL.CLI.Types.Java.Lang.Long(c));
|
||||
case IntrinsicType.Boolean:
|
||||
@@ -63,6 +67,9 @@ public sealed class AssemblyBuilder
|
||||
return _classes.GetOrAdd(javaClass, c => new ArrayOf(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));
|
||||
|
||||
var newClass = (NewClass)_classes.GetOrAdd(javaClass, _ =>
|
||||
{
|
||||
TypeAttributes flags = default;
|
||||
@@ -70,9 +77,16 @@ public sealed class AssemblyBuilder
|
||||
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 builder = _module.DefineType(javaClass.Name.ToString(), flags);
|
||||
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.Type.SetParent(MakeType(super).Type);
|
||||
|
||||
newClass.CreateFields();
|
||||
return newClass;
|
||||
}
|
||||
@@ -81,7 +95,18 @@ public sealed class AssemblyBuilder
|
||||
OpCodeReader opcodes)
|
||||
{
|
||||
var thisType = methodBase.DeclaringType!;
|
||||
Debug.Writer.WriteLine($"Compiling {thisType.FullName}.{methodBase.Name}");
|
||||
if (methodBase is MethodBuilder methodBuilder)
|
||||
{
|
||||
var ret = methodBuilder.ReturnType;
|
||||
var par = methodBuilder.GetParameters().Select(v => v.ParameterType.Name);
|
||||
var stc = methodBase.IsStatic ? "static " : string.Empty;
|
||||
Debug.Writer.WriteLine($"Compiling `{stc}{ret.Name} {thisType.FullName}.{methodBase.Name}({string.Join(", ", par)})`");
|
||||
}
|
||||
else
|
||||
{
|
||||
var par = methodBase.GetParameters().Select(v => v.ParameterType.Name);
|
||||
Debug.Writer.WriteLine($"Compiling `{thisType.FullName}({string.Join(", ", par)})`");
|
||||
}
|
||||
Debug.Writer.Indent++;
|
||||
|
||||
var labels = CreateLabels(il, opcodes);
|
||||
@@ -90,13 +115,25 @@ public sealed class AssemblyBuilder
|
||||
var locals = new Value[code.MaxLocals];
|
||||
var localCount = 0;
|
||||
|
||||
int AssignLocal(int idx, Value value)
|
||||
{
|
||||
if (value.Type == typeof(long) || value.Type == typeof(double))
|
||||
{
|
||||
locals[idx] = value;
|
||||
locals[idx + 1] = value;
|
||||
return 2;
|
||||
}
|
||||
locals[idx] = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
var idx = 0;
|
||||
if (!methodBase.IsStatic)
|
||||
locals[idx] = Value.Parameter(thisType, idx++);
|
||||
idx += AssignLocal(idx, Value.Parameter(thisType, idx));
|
||||
|
||||
foreach (var parameter in methodBase.GetParameters())
|
||||
locals[idx] = Value.Parameter(parameter.ParameterType, idx++);
|
||||
idx += AssignLocal(idx, Value.Parameter(parameter.ParameterType, idx));
|
||||
}
|
||||
|
||||
void CompileOpCode(long position, OpCode opcode)
|
||||
@@ -108,17 +145,36 @@ public sealed class AssemblyBuilder
|
||||
return labels[position + (short)opcode.P0.UShort];
|
||||
}
|
||||
|
||||
void CheckCast(Value value, Type type)
|
||||
void AssertCast(Value value, Type type)
|
||||
{
|
||||
if (value.Type == typeof(int) && type == typeof(byte))
|
||||
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;
|
||||
}
|
||||
else if (!value.Type.IsAssignableTo(type))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Value of type {value.Type} cannot be assigned to one of type {type}.");
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -185,10 +241,31 @@ public sealed class AssemblyBuilder
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
if (locals[idx].Type != typeof(int))
|
||||
throw new InvalidOperationException($"Local {idx} is not an integer.");
|
||||
ref var loc = ref locals[idx];
|
||||
AssertCast(loc, typeof(int));
|
||||
stack.Push(loc.Load(il));
|
||||
break;
|
||||
}
|
||||
|
||||
stack.Push(locals[idx].Load(il));
|
||||
case OpCodeMnemonic.LLoad:
|
||||
case OpCodeMnemonic.LLoad0:
|
||||
case OpCodeMnemonic.LLoad1:
|
||||
case OpCodeMnemonic.LLoad2:
|
||||
case OpCodeMnemonic.LLoad3:
|
||||
{
|
||||
var idx = opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.LLoad0 => 0,
|
||||
OpCodeMnemonic.LLoad1 => 1,
|
||||
OpCodeMnemonic.LLoad2 => 2,
|
||||
OpCodeMnemonic.LLoad3 => 3,
|
||||
OpCodeMnemonic.LLoad => opcode.P0.UShort,
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
ref var loc = ref locals[idx];
|
||||
AssertCast(loc, typeof(long));
|
||||
stack.Push(loc.Load(il));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -208,8 +285,8 @@ public sealed class AssemblyBuilder
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
if (locals[idx] is not { Type.IsClass: true })
|
||||
throw new InvalidOperationException($"Local {idx} is not a class.");
|
||||
if (locals[idx].Type is { IsClass: false, IsInterface: false })
|
||||
throw new InvalidOperationException($"Local {idx} ({locals[idx].Type}) is not a class.");
|
||||
|
||||
stack.Push(locals[idx].Load(il));
|
||||
break;
|
||||
@@ -217,12 +294,13 @@ public sealed class AssemblyBuilder
|
||||
|
||||
case OpCodeMnemonic.AALoad:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
var idx = stack.Pop();
|
||||
var array = stack.Pop();
|
||||
|
||||
if (!value.Type.IsArray)
|
||||
throw new InvalidOperationException("Value is not an array.");
|
||||
AssertIntCompatible(idx);
|
||||
AssertArray(array.Type);
|
||||
|
||||
if (value.Type.GetElementType() is not { IsClass: true } elementType)
|
||||
if (array.Type.GetElementType() is not { IsClass: true } elementType)
|
||||
throw new InvalidOperationException("Array element is not an object.");
|
||||
|
||||
il.Emit(OpCodes.Ldelem_Ref, elementType);
|
||||
@@ -230,6 +308,29 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IStore:
|
||||
case OpCodeMnemonic.IStore0:
|
||||
case OpCodeMnemonic.IStore1:
|
||||
case OpCodeMnemonic.IStore2:
|
||||
case OpCodeMnemonic.IStore3:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertCast(value, typeof(int));
|
||||
var idx = opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.IStore => opcode.P0.UShort,
|
||||
_ => opcode.Mnemonic - OpCodeMnemonic.IStore0,
|
||||
};
|
||||
ref var loc = ref locals[idx];
|
||||
if (loc.Type != typeof(int))
|
||||
{
|
||||
il.DeclareLocal(typeof(int));
|
||||
AssignLocal(idx, Value.Local(typeof(int), localCount++));
|
||||
}
|
||||
loc.Store(il);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.LStore:
|
||||
case OpCodeMnemonic.LStore0:
|
||||
case OpCodeMnemonic.LStore1:
|
||||
@@ -237,21 +338,59 @@ public sealed class AssemblyBuilder
|
||||
case OpCodeMnemonic.LStore3:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
CheckCast(value, typeof(long));
|
||||
ref var loc = ref locals[opcode.Mnemonic switch
|
||||
AssertCast(value, typeof(long));
|
||||
var idx = opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.LStore => opcode.P0.UShort,
|
||||
_ => opcode.Mnemonic - OpCodeMnemonic.LStore0,
|
||||
}];
|
||||
};
|
||||
ref var loc = ref locals[idx];
|
||||
if (loc.Type != typeof(long))
|
||||
{
|
||||
il.DeclareLocal(typeof(long));
|
||||
loc = Value.Local(typeof(long), localCount++);
|
||||
AssignLocal(idx, Value.Local(typeof(long), localCount++));
|
||||
}
|
||||
loc.Store(il);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.AStore:
|
||||
case OpCodeMnemonic.AStore0:
|
||||
case OpCodeMnemonic.AStore1:
|
||||
case OpCodeMnemonic.AStore2:
|
||||
case OpCodeMnemonic.AStore3:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertCast(value, typeof(object));
|
||||
var idx = opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.AStore => opcode.P0.UShort,
|
||||
_ => opcode.Mnemonic - OpCodeMnemonic.AStore0,
|
||||
};
|
||||
ref var loc = ref locals[idx];
|
||||
if (!value.Type.IsAssignableTo(loc.Type))
|
||||
{
|
||||
il.DeclareLocal(value.Type);
|
||||
AssignLocal(idx, Value.Local(value.Type, localCount++));
|
||||
}
|
||||
loc.Store(il);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.AAStore:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
var idx = stack.Pop();
|
||||
var array = stack.Pop();
|
||||
AssertArray(array);
|
||||
AssertIntCompatible(idx);
|
||||
|
||||
var eType = array.Type.GetElementType()!;
|
||||
AssertCast(value, eType);
|
||||
il.Emit(OpCodes.Stelem_Ref);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.Pop:
|
||||
{
|
||||
stack.Pop();
|
||||
@@ -268,12 +407,101 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IAdd:
|
||||
case OpCodeMnemonic.ISub:
|
||||
case OpCodeMnemonic.IMul:
|
||||
case OpCodeMnemonic.IDiv:
|
||||
case OpCodeMnemonic.IRem:
|
||||
case OpCodeMnemonic.IOr:
|
||||
case OpCodeMnemonic.IAnd:
|
||||
case OpCodeMnemonic.IShr:
|
||||
case OpCodeMnemonic.IShl:
|
||||
case OpCodeMnemonic.IUShr:
|
||||
{
|
||||
var a = stack.Pop();
|
||||
var b = stack.Pop();
|
||||
AssertIntCompatible(a.Type);
|
||||
AssertIntCompatible(b.Type);
|
||||
il.Emit(opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.IAdd => OpCodes.Add,
|
||||
OpCodeMnemonic.ISub => OpCodes.Sub,
|
||||
OpCodeMnemonic.IMul => OpCodes.Mul,
|
||||
OpCodeMnemonic.IDiv => OpCodes.Div,
|
||||
OpCodeMnemonic.IRem => OpCodes.Rem,
|
||||
OpCodeMnemonic.IOr => OpCodes.Or,
|
||||
OpCodeMnemonic.IAnd => OpCodes.And,
|
||||
OpCodeMnemonic.IShr => OpCodes.Shr,
|
||||
OpCodeMnemonic.IShl => OpCodes.Shl,
|
||||
OpCodeMnemonic.IUShr => OpCodes.Shr_Un,
|
||||
_ => throw new UnreachableException(),
|
||||
});
|
||||
stack.Push(new Value(typeof(int)));
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.LAdd:
|
||||
case OpCodeMnemonic.LSub:
|
||||
case OpCodeMnemonic.LMul:
|
||||
case OpCodeMnemonic.LDiv:
|
||||
case OpCodeMnemonic.LRem:
|
||||
case OpCodeMnemonic.LOr:
|
||||
case OpCodeMnemonic.LAnd:
|
||||
{
|
||||
var a = stack.Pop();
|
||||
var b = stack.Pop();
|
||||
AssertLongCompatible(a.Type);
|
||||
AssertLongCompatible(b.Type);
|
||||
il.Emit(opcode.Mnemonic switch
|
||||
{
|
||||
OpCodeMnemonic.LAdd => OpCodes.Add,
|
||||
OpCodeMnemonic.LSub => OpCodes.Sub,
|
||||
OpCodeMnemonic.LMul => OpCodes.Mul,
|
||||
OpCodeMnemonic.LDiv => OpCodes.Div,
|
||||
OpCodeMnemonic.LRem => OpCodes.Rem,
|
||||
OpCodeMnemonic.LOr => OpCodes.Or,
|
||||
OpCodeMnemonic.LAnd => OpCodes.And,
|
||||
_ => throw new UnreachableException(),
|
||||
});
|
||||
stack.Push(new Value(typeof(long)));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case OpCodeMnemonic.IfEq:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
if (value.Type != typeof(int) && value.Type != typeof(bool))
|
||||
throw new InvalidOperationException($"Expected integer, found {value.Type}.");
|
||||
AssertLongCompatible(value.Type);
|
||||
il.Emit(OpCodes.Brfalse, JmpLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IfNe:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertLongCompatible(value.Type);
|
||||
il.Emit(OpCodes.Brtrue, JmpLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IfLt:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertLongCompatible(value.Type);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Clt);
|
||||
il.Emit(OpCodes.Brtrue, JmpLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IfGe:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertLongCompatible(value.Type);
|
||||
il.Emit(OpCodes.Dup);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Cgt);
|
||||
il.Emit(OpCodes.Brtrue, JmpLabel());
|
||||
il.Emit(OpCodes.Brfalse, JmpLabel());
|
||||
break;
|
||||
}
|
||||
@@ -288,6 +516,12 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.Goto:
|
||||
{
|
||||
il.Emit(OpCodes.Br, JmpLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IReturn:
|
||||
case OpCodeMnemonic.LReturn:
|
||||
case OpCodeMnemonic.FReturn:
|
||||
@@ -335,7 +569,7 @@ public sealed class AssemblyBuilder
|
||||
var field = parent.GetField(fieldRef.Field.Name);
|
||||
var value = stack.Pop();
|
||||
|
||||
CheckCast(value, field.FieldType);
|
||||
AssertCast(value, field.FieldType);
|
||||
il.Emit(OpCodes.Stfld, field);
|
||||
break;
|
||||
}
|
||||
@@ -350,6 +584,7 @@ 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)));
|
||||
break;
|
||||
}
|
||||
@@ -394,6 +629,16 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.ANewArray:
|
||||
{
|
||||
var len = stack.Pop();
|
||||
AssertIntCompatible(len.Type);
|
||||
var type = MakeType(javaClass.GetConstant<Class>(opcode.P0.UShort));
|
||||
il.Emit(OpCodes.Newarr, type.Type);
|
||||
stack.Push(new Value(type.Type.MakeArrayType()));
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.NewArray:
|
||||
{
|
||||
var elementType = opcode.P0.Byte switch
|
||||
@@ -413,20 +658,25 @@ public sealed class AssemblyBuilder
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.ArrayLen:
|
||||
{
|
||||
var value = stack.Pop();
|
||||
AssertArray(value.Type);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
stack.Push(new Value(typeof(int)));
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IfNull:
|
||||
{
|
||||
if (!stack.Pop().Type.IsClass)
|
||||
throw new InvalidOperationException("Value is not an object.");
|
||||
|
||||
AssertClass(stack.Pop().Type);
|
||||
il.Emit(OpCodes.Brfalse, JmpLabel());
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.IfNonNull:
|
||||
{
|
||||
if (!stack.Pop().Type.IsClass)
|
||||
throw new InvalidOperationException("Value is not an object.");
|
||||
|
||||
AssertClass(stack.Pop().Type);
|
||||
il.Emit(OpCodes.Brtrue, JmpLabel());
|
||||
break;
|
||||
}
|
||||
@@ -468,6 +718,39 @@ public sealed class AssemblyBuilder
|
||||
opcodes.Reader.Stream.Position = start;
|
||||
return labels;
|
||||
}
|
||||
|
||||
private static void AssertClass(Type type)
|
||||
{
|
||||
if (type is { IsClass: false, IsInterface: false })
|
||||
throw new InvalidOperationException($"Value of type `{type}` is not an object.");
|
||||
}
|
||||
|
||||
private static void AssertArray(Type type)
|
||||
{
|
||||
if (!type.IsArray && type != typeof(Array))
|
||||
throw new InvalidOperationException($"Value of type `{type}` is not an array.");
|
||||
}
|
||||
|
||||
private static void AssertIntCompatible(Type type)
|
||||
{
|
||||
if(type == typeof(int)) return;
|
||||
if(type == typeof(byte)) return;
|
||||
if(type == typeof(char)) return;
|
||||
if(type == typeof(short)) return;
|
||||
if(type == typeof(bool)) return;
|
||||
throw new InvalidOperationException($"Value of type `{type}` is not an integer.");
|
||||
}
|
||||
|
||||
private static void AssertLongCompatible(Type type)
|
||||
{
|
||||
if(type == typeof(long)) return;
|
||||
if(type == typeof(int)) return;
|
||||
if(type == typeof(byte)) return;
|
||||
if(type == typeof(char)) return;
|
||||
if(type == typeof(short)) return;
|
||||
if(type == typeof(bool)) return;
|
||||
throw new InvalidOperationException($"Value of type `{type}` is not an integer.");
|
||||
}
|
||||
}
|
||||
|
||||
internal class LazyMethod(MethodBase method, Action compile)
|
||||
@@ -508,7 +791,7 @@ internal readonly struct Value
|
||||
|
||||
public void Store(ILGenerator il)
|
||||
{
|
||||
if (_store is not { } store) throw new InvalidOperationException("Value cannot be stored.");
|
||||
if (_store is not {} store) throw new InvalidOperationException("Value cannot be stored.");
|
||||
store(il, _data);
|
||||
}
|
||||
|
||||
@@ -651,4 +934,9 @@ internal readonly struct Value
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type.Name} {_data}";
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,12 @@ public class Array(Class javaClass) : TypeSurrogate
|
||||
|
||||
public sealed class ArrayOf(TypeSurrogate elementType, Class javaClass) : Array(javaClass)
|
||||
{
|
||||
public override Type Type => typeof(System.Array);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -44,6 +47,14 @@ public sealed class ArrayOf(TypeSurrogate elementType, Class javaClass) : Array(
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using JCIL.Java.Class;
|
||||
|
||||
namespace JCIL.CLI.Types.Java.Lang;
|
||||
|
||||
public sealed class Short(Class javaClass) : TypeSurrogate
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -320,6 +320,8 @@ public struct OpCode
|
||||
}
|
||||
|
||||
case OpCodeMnemonic.InvokeStatic:
|
||||
case OpCodeMnemonic.InvokeVirtual:
|
||||
case OpCodeMnemonic.InvokeSpecial:
|
||||
{
|
||||
var methodRef = javaClass.GetConstant<MethodRef>(P0.UShort);
|
||||
return $"{Mnemonic} {methodRef.Class}.{methodRef.Method.Name}";
|
||||
|
||||
Reference in New Issue
Block a user