230 lines
8.8 KiB
C#
230 lines
8.8 KiB
C#
![]() |
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using Mono.Cecil;
|
||
|
using Mono.Cecil.Cil;
|
||
|
using SpaceFlint.JavaBinary;
|
||
|
|
||
|
namespace SpaceFlint.CilToJava
|
||
|
{
|
||
|
|
||
|
public class CodeSpan
|
||
|
{
|
||
|
|
||
|
public static void Sizeof(object data, JavaCode code)
|
||
|
{
|
||
|
if (data is TypeReference operandTypeReference)
|
||
|
{
|
||
|
var myType = CilType.From(operandTypeReference);
|
||
|
if (myType.IsValueClass)
|
||
|
{
|
||
|
// compiler generated IL typically calculates the stackalloc
|
||
|
// total buffer size, for primitive types, and uses 'sizeof'
|
||
|
// for values types. but it also uses 'sizeof' for generic
|
||
|
// types, which may stand for a primitive type.
|
||
|
GenericUtil.LoadMaybeGeneric(myType, code);
|
||
|
code.NewInstruction(0xB8 /* invokestatic */, SpanType,
|
||
|
new JavaMethodRef("Sizeof" + CilMain.EXCLAMATION,
|
||
|
JavaType.IntegerType, CilType.SystemTypeType));
|
||
|
code.StackMap.PopStack(CilMain.Where); // generic type
|
||
|
code.StackMap.PushStack(JavaType.IntegerType);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
throw new InvalidProgramException();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static void Localloc(JavaCode code)
|
||
|
{
|
||
|
// 'localloc' step 1
|
||
|
|
||
|
// the 'localloc' instruction does not specify the type of the memory
|
||
|
// to allocate. in most cases it is followed by a store instruction
|
||
|
// into a variable of a pointer type, or of type System.Span, but in
|
||
|
// cases where the result of 'localloc' is passed to a method call,
|
||
|
// there can be any number of intermediate instruction that push
|
||
|
// other parameters, and it might not be practial to find the type.
|
||
|
|
||
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
||
|
code.StackMap.PushStack(CilType.SystemTypeType);
|
||
|
|
||
|
code.NewInstruction(0xB8 /* invokestatic */, SpanType,
|
||
|
new JavaMethodRef("Localloc" + CilMain.EXCLAMATION,
|
||
|
SpanType, JavaType.LongType, CilType.SystemTypeType));
|
||
|
|
||
|
code.StackMap.PopStack(CilMain.Where); // null type
|
||
|
code.StackMap.PopStack(CilMain.Where); // localloc size
|
||
|
code.StackMap.PushStack(SpanType);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static bool AddOffset(JavaType offsetType, JavaType spanType, JavaCode code)
|
||
|
{
|
||
|
if (spanType.Equals(SpanType))
|
||
|
{
|
||
|
code.StackMap.PushStack(spanType);
|
||
|
|
||
|
if (offsetType.Equals(JavaType.IntegerType))
|
||
|
{
|
||
|
code.NewInstruction(0x85 /* i2l */, null, null);
|
||
|
offsetType = JavaType.LongType;
|
||
|
}
|
||
|
code.StackMap.PushStack(offsetType);
|
||
|
|
||
|
bool loadedType = false;
|
||
|
if (spanType is CilType spanType2)
|
||
|
{
|
||
|
if ( (! spanType2.HasGenericParameters)
|
||
|
&& spanType2.GenericParameters != null
|
||
|
&& spanType2.GenericParameters[0] is CilType spanPointerType
|
||
|
&& (! spanPointerType.IsGenericParameter))
|
||
|
{
|
||
|
GenericUtil.LoadMaybeGeneric(spanPointerType, code);
|
||
|
loadedType = true;
|
||
|
}
|
||
|
}
|
||
|
if (! loadedType)
|
||
|
{
|
||
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
||
|
code.StackMap.PushStack(CilType.SystemTypeType);
|
||
|
}
|
||
|
|
||
|
code.NewInstruction(0xB6 /* invokevirtual */, SpanType,
|
||
|
new JavaMethodRef("Add",
|
||
|
SpanType, offsetType, CilType.SystemTypeType));
|
||
|
|
||
|
code.StackMap.PopStack(CilMain.Where); // span type
|
||
|
code.StackMap.PopStack(CilMain.Where); // offset
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static bool Box(CilType intoType, JavaType spanType, JavaCode code)
|
||
|
{
|
||
|
if (spanType.Equals(SpanType) && intoType.IsByReference)
|
||
|
{
|
||
|
code.NewInstruction(0xB6 /* invokevirtual */, SpanType,
|
||
|
new JavaMethodRef("Box", CilType.SystemValueType));
|
||
|
code.StackMap.PushStack(CilType.SystemValueType);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static bool Address(CilType fromType, CilType intoType, JavaCode code)
|
||
|
{
|
||
|
if (intoType.Equals(CodeSpan.SpanType) && (! fromType.Equals(CodeSpan.SpanType)))
|
||
|
{
|
||
|
if ( fromType.Equals(JavaType.StringType)
|
||
|
&& intoType.GenericParameters != null
|
||
|
&& intoType.GenericParameters[0].Equals(fromType))
|
||
|
{
|
||
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
||
|
code.StackMap.PushStack(CilType.SystemTypeType);
|
||
|
|
||
|
code.NewInstruction(0xB8 /* invokestatic */, SpanType,
|
||
|
new JavaMethodRef("String" + CilMain.EXCLAMATION,
|
||
|
SpanType, JavaType.StringType, CilType.SystemTypeType));
|
||
|
|
||
|
code.StackMap.PopStack(CilMain.Where); // null type
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (fromType.Equals(JavaStackMap.Null))
|
||
|
return true;
|
||
|
|
||
|
throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer");
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static bool LoadStore(bool isLoad, CilType stackTop, JavaType opcodeType, JavaCode code)
|
||
|
{
|
||
|
if (stackTop.Equals(SpanType) && code.Method.Class.Name != SpanType.ClassName)
|
||
|
{
|
||
|
string opcodeDescr;
|
||
|
if (opcodeType == null)
|
||
|
{
|
||
|
opcodeType = JavaType.ObjectType;
|
||
|
opcodeDescr = "";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( opcodeType.Equals(JavaType.ShortType)
|
||
|
&& stackTop.GenericParameters != null
|
||
|
&& stackTop.GenericParameters[0].Equals(JavaType.CharacterType))
|
||
|
{
|
||
|
opcodeType = JavaType.CharacterType;
|
||
|
}
|
||
|
|
||
|
opcodeDescr = opcodeType.ToDescriptor();
|
||
|
}
|
||
|
|
||
|
var voidType = JavaType.VoidType;
|
||
|
var spanMethod = isLoad
|
||
|
? (new JavaMethodRef("Load" + opcodeDescr, opcodeType))
|
||
|
: (new JavaMethodRef("Store" + opcodeDescr, voidType, opcodeType));
|
||
|
code.NewInstruction(0xB6 /* invokevirtual */, SpanType, spanMethod);
|
||
|
if (isLoad)
|
||
|
code.StackMap.PushStack(CilType.From(opcodeType));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
public static void Compare(JavaType stackTop, JavaType stackTop2,
|
||
|
Mono.Cecil.Cil.Instruction cilInst, JavaCode code)
|
||
|
{
|
||
|
if ( stackTop.Equals(CodeSpan.SpanType)
|
||
|
&& ( stackTop2.PrimitiveType == TypeCode.Int64
|
||
|
|| stackTop2.PrimitiveType == TypeCode.UInt64))
|
||
|
{
|
||
|
// compare Span with long
|
||
|
throw new InvalidProgramException();
|
||
|
}
|
||
|
|
||
|
if ( stackTop2.Equals(CodeSpan.SpanType)
|
||
|
&& ( stackTop.PrimitiveType == TypeCode.Int64
|
||
|
|| stackTop.PrimitiveType == TypeCode.UInt64))
|
||
|
{
|
||
|
if ( cilInst.Previous == null
|
||
|
|| cilInst.Previous.OpCode.Code != Code.Conv_U
|
||
|
|| cilInst.Previous.Previous == null
|
||
|
|| cilInst.Previous.Previous.OpCode.Code != Code.Ldc_I4_0)
|
||
|
{
|
||
|
// make sure the program is comparing the span address against
|
||
|
// a zero value, which we can convert to a null reference.
|
||
|
// ldarg.1 (span argument)
|
||
|
// ldc.i4.0
|
||
|
// conv.u
|
||
|
// bne.un label
|
||
|
throw new InvalidProgramException();
|
||
|
}
|
||
|
// compare long with Span
|
||
|
code.NewInstruction(0x58 /* pop2 */, null, null);
|
||
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
internal static readonly CilType SpanType =
|
||
|
CilType.From(new JavaType(0, 0, "system.Span$$1"));
|
||
|
}
|
||
|
|
||
|
}
|