bluebonnet/CilToJava/src/CodeSpan.cs

353 lines
14 KiB
C#
Raw Normal View History

2020-08-26 11:23:24 +03:00
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;
}
2020-09-01 18:28:45 +03:00
public static bool SubOffset(JavaType secondType, JavaCode code)
{
// make sure the first operand is a pointer span, not a real Span<T>
var spanType1 = (CilType) code.StackMap.PopStack(CilMain.Where);
if ( (! spanType1.HasGenericParameters)
&& spanType1.GenericParameters != null
&& spanType1.GenericParameters[0] is CilType span1PointerType
&& (! span1PointerType.IsGenericParameter))
{
var spanType2 = (CilType) secondType;
// check if subtracting two pointer spans
if ( (! spanType2.HasGenericParameters)
&& spanType2.GenericParameters != null
&& spanType2.GenericParameters[0] is CilType span2PointerType
&& (! span2PointerType.IsGenericParameter))
{
code.NewInstruction(0xB6 /* invokevirtual */, SpanType,
new JavaMethodRef("Subtract",
JavaType.LongType, spanType2));
code.StackMap.PushStack(CilType.From(JavaType.LongType));
return true;
}
// check if subtracting an offset from a pointer span
if (spanType2.Equals(JavaType.IntegerType))
{
code.NewInstruction(0xB6 /* invokevirtual */, SpanType,
new JavaMethodRef("Subtract",
SpanType, spanType2));
code.StackMap.PushStack(SpanType);
return true;
}
}
code.StackMap.PushStack(spanType1);
return false;
}
2020-08-26 11:23:24 +03:00
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)
{
2020-09-01 18:28:45 +03:00
if (intoType.Equals(SpanType) && (! fromType.Equals(SpanType)))
2020-08-26 11:23:24 +03:00
{
2020-09-01 18:28:45 +03:00
// allow assignment of null to clear the pointer
if (fromType.Equals(JavaStackMap.Null))
return true;
// allow assignment of native int (presumably zero)
bool callAssign = false;
bool pushNullType = true;
JavaType argType = fromType;
JavaType retType = SpanType;
if ((! fromType.IsReference) && fromType.PrimitiveType == TypeCode.UInt64)
callAssign = true;
else if (intoType.GenericParameters != null)
2020-08-26 11:23:24 +03:00
{
2020-09-01 18:28:45 +03:00
// allow assignment when the types match
callAssign = intoType.GenericParameters[0].Equals(fromType)
|| fromType.JavaName == intoType.GenericParameters[0].JavaName;
// for arbitrary value types, call a Assign(ValueType)
if (fromType.IsValueClass)
{
argType = retType = CilType.SystemValueType;
GenericUtil.LoadMaybeGeneric(fromType, code);
pushNullType = false;
}
}
if (callAssign)
{
if (pushNullType)
{
code.NewInstruction(0x01 /* aconst_null */, null, null);
code.StackMap.PushStack(CilType.SystemTypeType);
}
2020-08-26 11:23:24 +03:00
code.NewInstruction(0xB8 /* invokestatic */, SpanType,
2020-09-01 18:28:45 +03:00
new JavaMethodRef("Assign" + CilMain.EXCLAMATION,
retType, argType, CilType.SystemTypeType));
code.NewInstruction(0xC0 /* checkcast */, SpanType, null);
2020-08-26 11:23:24 +03:00
code.StackMap.PopStack(CilMain.Where); // null type
return true;
}
2020-09-01 18:28:45 +03:00
throw new Exception($"bad assignment of '{fromType.JavaName}' into pointer of '{intoType.GenericParameters[0].JavaName}'");
2020-08-26 11:23:24 +03:00
}
return false;
}
2020-09-01 18:28:45 +03:00
public static bool LoadStore(bool isLoad, CilType stackTop, JavaType opcodeType,
CilType dataType, JavaCode code)
2020-08-26 11:23:24 +03:00
{
if (stackTop.Equals(SpanType) && code.Method.Class.Name != SpanType.ClassName)
{
string opcodeDescr;
if (opcodeType == null)
{
2020-09-01 18:28:45 +03:00
opcodeType = CilType.SystemValueType;
2020-08-26 11:23:24 +03:00
opcodeDescr = "";
2020-09-01 18:28:45 +03:00
// at this point we should have been called from LoadObject or
// StoreObject in CodeMisc to handle a ldobj/stobj instruction,
// so make sure the pointer-span element is a value type
if (dataType.IsGenericParameter || (! dataType.IsValueClass))
throw new InvalidProgramException();
code.NewInstruction(0x12 /* ldc */, dataType.AsWritableClass, null);
// make sure the stack has room for three parameters:
// 'this', value reference (in case of Store), and class
code.StackMap.PushStack(JavaType.ObjectType);
code.StackMap.PushStack(JavaType.ObjectType);
code.StackMap.PushStack(JavaType.ObjectType);
code.StackMap.PopStack(CilMain.Where);
code.StackMap.PopStack(CilMain.Where);
code.StackMap.PopStack(CilMain.Where);
2020-08-26 11:23:24 +03:00
}
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));
2020-09-01 18:28:45 +03:00
if (opcodeDescr == "")
spanMethod.Parameters.Add(new JavaFieldRef("", JavaType.ClassType));
2020-08-26 11:23:24 +03:00
code.NewInstruction(0xB6 /* invokevirtual */, SpanType, spanMethod);
if (isLoad)
code.StackMap.PushStack(CilType.From(opcodeType));
return true;
}
return false;
}
2020-09-01 18:28:45 +03:00
public static bool Clear(JavaType stackTop, CilType dataType, JavaCode code)
{
if ( stackTop.Equals(SpanType)
&& dataType.IsValueClass
&& code.Method.Class.Name != SpanType.ClassName)
{
// if initobj is called on a span or pointer, call Span<T>.Clear()
code.NewInstruction(0xB6 /* invokevirtual */, SpanType,
new JavaMethodRef("Clear", JavaType.VoidType));
return true;
}
return false;
}
public static void CompareEq(JavaType stackTop, JavaType stackTop2,
Mono.Cecil.Cil.Instruction cilInst, JavaCode code)
2020-08-26 11:23:24 +03:00
{
2020-09-01 18:28:45 +03:00
if ( stackTop.Equals(SpanType)
2020-08-26 11:23:24 +03:00
&& ( stackTop2.PrimitiveType == TypeCode.Int64
|| stackTop2.PrimitiveType == TypeCode.UInt64))
{
// compare Span with long
throw new InvalidProgramException();
}
2020-09-01 18:28:45 +03:00
if ( stackTop2.Equals(SpanType)
2020-08-26 11:23:24 +03:00
&& ( 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);
}
}
2020-09-01 18:28:45 +03:00
public static bool CompareGtLt(JavaType stackTop, JavaType stackTop2, JavaCode code)
{
if (stackTop.Equals(SpanType) && stackTop2.Equals(SpanType))
{
code.NewInstruction(0x01 /* aconst_null */, null, null);
code.StackMap.PushStack(CilType.SystemTypeType);
code.NewInstruction(0xB8 /* invokestatic */, SpanType,
new JavaMethodRef("CompareTo" + CilMain.EXCLAMATION,
JavaType.IntegerType, SpanType, SpanType, CilType.SystemTypeType));
code.StackMap.PopStack(CilMain.Where); // null type
return true;
}
return false;
}
2020-08-26 11:23:24 +03:00
internal static readonly CilType SpanType =
CilType.From(new JavaType(0, 0, "system.Span$$1"));
}
}