820 lines
33 KiB
C#
820 lines
33 KiB
C#
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using SpaceFlint.JavaBinary;
|
|
|
|
namespace SpaceFlint.CilToJava
|
|
{
|
|
|
|
public class CodeArrays
|
|
{
|
|
|
|
CodeLocals locals;
|
|
JavaCode code;
|
|
JavaStackMap stackMap;
|
|
|
|
|
|
|
|
public CodeArrays(JavaCode _code, CodeLocals _locals)
|
|
{
|
|
locals = _locals;
|
|
code = _code;
|
|
stackMap = code.StackMap;
|
|
}
|
|
|
|
|
|
|
|
public void Length()
|
|
{
|
|
var stackTop = (CilType) stackMap.PopStack(CilMain.Where);
|
|
if (object.ReferenceEquals(stackTop, GenericArrayType))
|
|
{
|
|
// length of a generic array T[]
|
|
code.NewInstruction(0xB8 /* invokestatic */,
|
|
new JavaType(0, 0, "java.lang.reflect.Array"),
|
|
new JavaMethodRef("getLength", JavaType.IntegerType, JavaType.ObjectType));
|
|
}
|
|
else if (stackTop.IsArray)
|
|
code.NewInstruction(0xBE /* arraylength */, null, null);
|
|
else
|
|
throw new InvalidProgramException();
|
|
stackMap.PushStack(CilType.From(JavaType.IntegerType));
|
|
}
|
|
|
|
|
|
|
|
public void New(object data)
|
|
{
|
|
if (data is TypeReference cilType)
|
|
{
|
|
var elemType = CilMain.GenericStack.EnterType(cilType);
|
|
bool arrayTypeOnStack = false;
|
|
|
|
int numDims = 0;
|
|
while (data is ArrayType dataArrayType)
|
|
{
|
|
numDims += dataArrayType.Rank;
|
|
data = dataArrayType.ElementType;
|
|
}
|
|
|
|
if (numDims > 0)
|
|
{
|
|
if (data is GenericParameter)
|
|
{
|
|
GenericUtil.LoadMaybeGeneric(elemType, code);
|
|
|
|
code.NewInstruction(0x12 /* ldc */, null, numDims);
|
|
code.StackMap.PushStack(JavaType.IntegerType);
|
|
|
|
code.NewInstruction(0xB6 /* invokevirtual */, CilType.SystemTypeType,
|
|
new JavaMethodRef("MakeArrayType",
|
|
CilType.SystemTypeType, JavaType.IntegerType));
|
|
|
|
stackMap.PopStack(CilMain.Where); // number of dimensions
|
|
|
|
arrayTypeOnStack = true;
|
|
}
|
|
|
|
else if (numDims > 1)
|
|
elemType = elemType.AdjustRank(numDims - 1);
|
|
}
|
|
|
|
New(elemType, 1, arrayTypeOnStack);
|
|
}
|
|
else
|
|
throw new InvalidProgramException();
|
|
}
|
|
|
|
|
|
|
|
public void New(CilType elemType, int numDims, bool arrayTypeOnStack = false)
|
|
{
|
|
var elemTypeForArray = elemType.IsGenericParameter
|
|
? CilType.From(JavaType.ObjectType) : elemType;
|
|
var arrayType = elemTypeForArray.AdjustRank(numDims);
|
|
|
|
if (elemType.IsGenericParameter)
|
|
{
|
|
/*if (numDims != 1)
|
|
throw new Exception("unsupported number of dimensions in generic array");*/
|
|
|
|
if (! arrayTypeOnStack)
|
|
GenericUtil.LoadMaybeGeneric(elemType, code);
|
|
|
|
var parameters = new List<JavaFieldRef>();
|
|
for (int i = 0; i < numDims; i++)
|
|
parameters.Add(new JavaFieldRef("", JavaType.IntegerType));
|
|
parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));
|
|
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType,
|
|
new JavaMethodRef("New", JavaType.ObjectType, parameters));
|
|
|
|
stackMap.PopStack(CilMain.Where); // type
|
|
|
|
while (numDims-- > 0)
|
|
stackMap.PopStack(CilMain.Where);
|
|
|
|
arrayType = GenericArrayType;
|
|
}
|
|
|
|
else if (elemType.IsReference || numDims > 1)
|
|
{
|
|
if (numDims == 1)
|
|
code.NewInstruction(0xBD /* anewarray */, elemType, null);
|
|
else
|
|
code.NewInstruction(0xC5 /* multianewarray */, arrayType, numDims);
|
|
|
|
for (int i = 0; i < numDims; i++)
|
|
stackMap.PopStack(CilMain.Where);
|
|
|
|
stackMap.PushStack(arrayType); // arrayObj
|
|
|
|
if (elemType.ArrayRank != 0)
|
|
{
|
|
if (numDims == 1)
|
|
{
|
|
// notify the array support methods in baselib that this
|
|
// is a jagged array, i.e. a single dimension array with
|
|
// an element type that is also an array. for arrays of
|
|
// generic type, see system.Array.New() in baselib.
|
|
|
|
stackMap.PushStack(JavaType.ObjectType);
|
|
code.NewInstruction(0x59 /* dup */, null, null);
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType,
|
|
new JavaMethodRef("MarkJagged",
|
|
JavaType.VoidType, JavaType.ObjectType));
|
|
stackMap.PopStack(CilMain.Where);
|
|
}
|
|
}
|
|
|
|
else if (elemType.IsValueClass)
|
|
{
|
|
code.NewInstruction(0x59 /* dup */, null, null);
|
|
stackMap.PushStack(arrayType); // arrayCopy
|
|
|
|
if (elemType.HasGenericParameters)
|
|
{
|
|
// array of a value class ArrayElement<T>, pass this type
|
|
// as the second parameter to system.Array.Initialize
|
|
|
|
GenericUtil.LoadMaybeGeneric(elemType, code);
|
|
|
|
// third parameter is null
|
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
|
stackMap.PushStack(JavaType.ObjectType);
|
|
}
|
|
else
|
|
{
|
|
// array of a plain value class, pass a constructed object
|
|
// as the third parameter to system.Array.Initialize
|
|
|
|
// second parameter is null
|
|
code.NewInstruction(0x01 /* aconst_null */, null, null);
|
|
stackMap.PushStack(CilType.SystemTypeType);
|
|
|
|
// model parameter is a new object
|
|
code.NewInstruction(0xBB /* new */, elemType.AsWritableClass, null);
|
|
stackMap.PushStack(elemType);
|
|
code.NewInstruction(0x59 /* dup */, null, null);
|
|
stackMap.PushStack(elemType);
|
|
code.NewInstruction(0xB7 /* invokespecial */, elemType.AsWritableClass,
|
|
new CilMethod(elemType));
|
|
stackMap.PopStack(CilMain.Where);
|
|
}
|
|
|
|
code.NewInstruction(0x12 /* ldc */, null, numDims);
|
|
stackMap.PushStack(JavaType.IntegerType);
|
|
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, InitArrayMethod);
|
|
|
|
stackMap.PopStack(CilMain.Where); // numDims
|
|
stackMap.PopStack(CilMain.Where); // elemType
|
|
stackMap.PopStack(CilMain.Where); // arrayType
|
|
stackMap.PopStack(CilMain.Where); // arrayCopy
|
|
}
|
|
|
|
stackMap.PopStack(CilMain.Where); // arrayObj
|
|
}
|
|
else
|
|
{
|
|
if (numDims == 1)
|
|
{
|
|
var length = stackMap.PopStack(CilMain.Where);
|
|
stackMap.PushStack(length);
|
|
if (length.Equals(JavaType.LongType))
|
|
CodeNumber.Conversion(code, Code.Conv_Ovf_I4, null);
|
|
}
|
|
|
|
code.NewInstruction(0xBC /* newarray */, null, elemType.NewArrayType);
|
|
|
|
while (numDims-- > 0)
|
|
stackMap.PopStack(CilMain.Where);
|
|
}
|
|
|
|
stackMap.PushStack(arrayType);
|
|
}
|
|
|
|
|
|
|
|
public void Load(Code op, object data, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
stackMap.PopStack(CilMain.Where); // pop index
|
|
CilType arrayType = (CilType) stackMap.PopStack(CilMain.Where);
|
|
CilType elemType = null;
|
|
TypeCode elemCode = 0;
|
|
|
|
switch (op)
|
|
{
|
|
case Code.Ldelem_Ref:
|
|
if (arrayType.IsValueClass)
|
|
throw new InvalidProgramException();
|
|
elemType = arrayType.AdjustRank(-1);
|
|
break;
|
|
|
|
case Code.Ldelem_Any:
|
|
|
|
//if (data is ArrayType || (! (data is TypeReference)))
|
|
if (! (data is TypeReference))
|
|
throw new InvalidProgramException();
|
|
elemType = CilType.From((TypeReference) data);
|
|
|
|
// check if array element is loaded and boxed, only to check
|
|
// its type or check if null. in this case we take a shortcut
|
|
// and load directly from the array, without making a copy.
|
|
|
|
var next = inst.Next;
|
|
if (next != null && next.OpCode.Code == Code.Box)
|
|
{
|
|
next = next.Next;
|
|
if (next != null && CodeBuilder.IsBrTrueBrFalseIsInst(next.OpCode.Code))
|
|
{
|
|
code.NewInstruction(0xB8 /* invokestatic */,
|
|
new JavaType(0, 0, "java.lang.reflect.Array"),
|
|
new JavaMethodRef("get", JavaType.ObjectType, JavaType.ObjectType, JavaType.IntegerType));
|
|
stackMap.PushStack(elemType);
|
|
return;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case Code.Ldelem_I1: elemCode = TypeCode.SByte; break;
|
|
case Code.Ldelem_U1: elemCode = TypeCode.Byte; break;
|
|
case Code.Ldelem_U2: elemCode = TypeCode.Char; break;
|
|
case Code.Ldelem_I2: elemCode = TypeCode.Int16; break;
|
|
case Code.Ldelem_I4: case Code.Ldelem_U4: elemCode = TypeCode.Int32; break;
|
|
case Code.Ldelem_I8: case Code.Ldelem_I: elemCode = TypeCode.Int64; break;
|
|
case Code.Ldelem_R4: elemCode = TypeCode.Single; break;
|
|
case Code.Ldelem_R8: elemCode = TypeCode.Double; break;
|
|
|
|
default: throw new InvalidProgramException();
|
|
}
|
|
|
|
if (elemCode != 0)
|
|
elemType = CilType.From(new JavaType(elemCode, 0, null));
|
|
Load(arrayType, elemType, inst);
|
|
}
|
|
|
|
|
|
|
|
void Load(CilType arrayType, CilType elemType, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
if (arrayType == null)
|
|
arrayType = elemType.AdjustRank(1);
|
|
/*Console.WriteLine("(LOAD) ARRAY TYPE = " + arrayType + "," + arrayType.ArrayRank
|
|
+ " ELEM TYPE = " + elemType + "," + elemType.ArrayRank
|
|
+ " ELEMVAL? " + elemType.IsValueClass
|
|
+ " ELEMGEN? " + elemType.IsGenericParameter);*/
|
|
|
|
if ( object.ReferenceEquals(arrayType, GenericArrayType)
|
|
|| elemType.IsGenericParameter || arrayType.IsGenericParameter)
|
|
{
|
|
code.NewInstruction(0xB8 /* invokestatic */,
|
|
SystemArrayType, LoadArrayMethod);
|
|
if (elemType.ArrayRank != 0)
|
|
elemType = GenericArrayType;
|
|
}
|
|
else
|
|
{
|
|
if ( elemType.PrimitiveType == TypeCode.Int16
|
|
&& ( arrayType.PrimitiveType == TypeCode.Char
|
|
|| arrayType.PrimitiveType == TypeCode.UInt16))
|
|
{
|
|
// ldelem.i2 with a char[] array, should be 'caload' not 'saload'
|
|
elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
|
|
}
|
|
|
|
code.NewInstruction(elemType.LoadArrayOpcode, null, null);
|
|
|
|
if (elemType.PrimitiveType == TypeCode.Byte)
|
|
{
|
|
// unsigned byte result should be truncated to 8-bits
|
|
// (unless already followed by "ldc.i4 255 ; and")
|
|
bool followedByAndWith255 =
|
|
CodeBuilder.IsLoadConstant(inst.Next) == 0xFF
|
|
&& inst.Next.Next?.OpCode.Code == Code.And;
|
|
|
|
if (! followedByAndWith255)
|
|
{
|
|
stackMap.PushStack(JavaType.IntegerType);
|
|
code.NewInstruction(0x12 /* ldc */, null, (int) 0xFF);
|
|
code.NewInstruction(0x7E /* iand */, null, null);
|
|
stackMap.PopStack(CilMain.Where);
|
|
}
|
|
}
|
|
|
|
if (arrayType.IsValueClass || elemType.IsValueClass)
|
|
{
|
|
CilMethod.ValueMethod(CilMethod.ValueClone, code);
|
|
if (elemType.IsValueClass)
|
|
code.NewInstruction(0xC0 /* checkcast */, elemType, null);
|
|
}
|
|
}
|
|
|
|
stackMap.PushStack(elemType);
|
|
}
|
|
|
|
|
|
|
|
public void Store(Code op, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
TypeCode elemType;
|
|
|
|
switch (op)
|
|
{
|
|
case Code.Stelem_Ref: case Code.Stelem_Any:
|
|
|
|
Store(null, null);
|
|
return;
|
|
|
|
case Code.Stelem_I1: elemType = TypeCode.Byte; break;
|
|
case Code.Stelem_I2: elemType = TypeCode.Int16; break;
|
|
case Code.Stelem_I4: elemType = TypeCode.Int32; break;
|
|
case Code.Stelem_I8: case Code.Stelem_I: elemType = TypeCode.Int64; break;
|
|
case Code.Stelem_R4: elemType = TypeCode.Single; break;
|
|
case Code.Stelem_R8: elemType = TypeCode.Double; break;
|
|
|
|
default: throw new InvalidProgramException();
|
|
}
|
|
|
|
Store(CilType.From(new JavaType(elemType, 0, null)), inst);
|
|
}
|
|
|
|
|
|
|
|
void Store(CilType elemType, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
stackMap.PopStack(CilMain.Where); // value
|
|
stackMap.PopStack(CilMain.Where); // index
|
|
var arrayType = stackMap.PopStack(CilMain.Where) as CilType; // array
|
|
var arrayElemType = arrayType.AdjustRank(-arrayType.ArrayRank);
|
|
if (elemType == null)
|
|
elemType = arrayElemType;
|
|
|
|
/*Console.WriteLine("(STORE) ARRAY TYPE = " + arrayType + "," + arrayType.ArrayRank
|
|
+ " ELEM TYPE = " + elemType + "," + elemType.ArrayRank
|
|
+ " ELEMVAL? " + elemType.IsValueClass
|
|
+ " ELEMGEN? " + elemType.IsGenericParameter);*/
|
|
|
|
if ( object.ReferenceEquals(arrayType, GenericArrayType))
|
|
{
|
|
// stelem.any T into generic array T[]
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, StoreArrayMethod);
|
|
}
|
|
else if (arrayElemType.IsValueClass && elemType.IsValueClass)
|
|
{
|
|
// storing a value type into an array of value types.
|
|
// we use ValueType.ValueCopy to write over the element.
|
|
|
|
int localIndex = locals.GetTempIndex(elemType);
|
|
code.NewInstruction(elemType.StoreOpcode, null, localIndex);
|
|
|
|
code.NewInstruction(arrayType.LoadArrayOpcode, null, null);
|
|
|
|
code.NewInstruction(elemType.LoadOpcode, null, localIndex);
|
|
locals.FreeTempIndex(localIndex);
|
|
|
|
// we can pass any type that is not a generic parameter
|
|
GenericUtil.ValueCopy(CilType.SystemTypeType, code, true);
|
|
}
|
|
else if (arrayType.ArrayRank > 1)
|
|
{
|
|
// always 'aastore' if multidimensional array
|
|
code.NewInstruction(arrayType.StoreArrayOpcode, null, null);
|
|
}
|
|
else
|
|
{
|
|
if ( elemType.PrimitiveType == TypeCode.Int16
|
|
&& ( arrayType.PrimitiveType == TypeCode.Char
|
|
|| arrayType.PrimitiveType == TypeCode.UInt16))
|
|
{
|
|
// stelem.i2 with a char[] array, should be 'castore' not 'sastore'
|
|
elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
|
|
}
|
|
else
|
|
{
|
|
// Android AOT crashes the compilation if an immediate value
|
|
// is stored into a byte or short array, and the value does
|
|
// not fit within the range -128..127 or -32768..32767.
|
|
// simply checing if the previous instruction loaded the
|
|
// constant is not enough, because due to method inlining
|
|
// by the Android ART JIT, the immediate value might actually
|
|
// originate in a calling method.
|
|
// so we always force the value into range using i2b/i2s.
|
|
// see also: CodeNumber::ConvertToInteger
|
|
|
|
if ( arrayType.PrimitiveType == TypeCode.Boolean
|
|
|| arrayType.PrimitiveType == TypeCode.SByte
|
|
|| arrayType.PrimitiveType == TypeCode.Byte)
|
|
{
|
|
code.NewInstruction(0x91 /* i2b */, null, null);
|
|
}
|
|
else if ( arrayType.PrimitiveType == TypeCode.Int16)
|
|
{
|
|
code.NewInstruction(0x93 /* i2s */, null, null);
|
|
}
|
|
}
|
|
|
|
if (arrayType.IsValueClass || elemType.IsValueClass)
|
|
{
|
|
CilMethod.ValueMethod(CilMethod.ValueClone, code);
|
|
}
|
|
|
|
code.NewInstruction(elemType.StoreArrayOpcode, null, null);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void Address(CilType arrayType, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
stackMap.PopStack(CilMain.Where); // index
|
|
|
|
if (arrayType == null)
|
|
arrayType = (CilType) stackMap.PopStack(CilMain.Where);
|
|
else
|
|
stackMap.PopStack(CilMain.Where); // array
|
|
|
|
var elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
|
|
|
|
if (inst != null && inst.Next != null)
|
|
{
|
|
var (spanType, _) = locals.GetLocalFromStoreInst(
|
|
inst.Next.OpCode.Code, inst.Next.Operand);
|
|
|
|
if (CodeSpan.AddressArray(elemType, spanType, code))
|
|
return;
|
|
}
|
|
|
|
if (elemType.IsReference)
|
|
{
|
|
if (elemType.IsGenericParameter)
|
|
{
|
|
// call system.Array.Box(object array, int index)
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType,
|
|
new JavaMethodRef("Box", CilType.SystemValueType,
|
|
JavaType.ObjectType, JavaType.IntegerType));
|
|
}
|
|
else if (elemType.IsValueClass)
|
|
{
|
|
code.NewInstruction(0x32 /* aaload */, null, null);
|
|
}
|
|
else
|
|
{
|
|
// call system.Reference.Box(object a, int i)
|
|
elemType = new BoxedType(elemType, false);
|
|
code.NewInstruction(0xB8 /* invokestatic */, elemType,
|
|
new JavaMethodRef("Box", elemType,
|
|
JavaType.ObjectType, JavaType.IntegerType));
|
|
}
|
|
stackMap.PushStack(elemType);
|
|
}
|
|
else
|
|
{
|
|
// call system.(PrimitiveType).Box(primitiveType[] a, int i)
|
|
var typeCode = elemType.PrimitiveType;
|
|
|
|
stackMap.PushStack(new BoxedType(elemType, false));
|
|
|
|
arrayType = elemType.AdjustRank(1);
|
|
elemType = elemType.AsWritableClass;
|
|
code.NewInstruction(0xB8 /* invokestatic */, elemType,
|
|
new JavaMethodRef("Box", elemType, arrayType, JavaType.IntegerType));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void Call(CilMethod method, Mono.Cecil.Cil.Instruction inst)
|
|
{
|
|
int numDims = method.Parameters.Count;
|
|
var elemType = method.DeclType;
|
|
|
|
if (method.Name == "Get")
|
|
{
|
|
Deref(numDims);
|
|
|
|
stackMap.PopStack(CilMain.Where); // pop index
|
|
stackMap.PopStack(CilMain.Where); // pop array
|
|
Load(null, elemType, inst);
|
|
}
|
|
|
|
else if (method.Name == "Set")
|
|
{
|
|
numDims--; // last parameter is value
|
|
if (numDims > 1)
|
|
{
|
|
var valueType = stackMap.PopStack(CilMain.Where);
|
|
int localIndex = locals.GetTempIndex(valueType);
|
|
code.NewInstruction(valueType.StoreOpcode, null, localIndex);
|
|
|
|
Deref(numDims);
|
|
|
|
code.NewInstruction(valueType.LoadOpcode, null, localIndex);
|
|
stackMap.PushStack(valueType);
|
|
}
|
|
|
|
Store(elemType, null);
|
|
}
|
|
|
|
else if (method.Name == "Address")
|
|
{
|
|
Deref(numDims);
|
|
|
|
Address(elemType, null);
|
|
}
|
|
|
|
else
|
|
throw new InvalidProgramException();
|
|
}
|
|
|
|
|
|
|
|
void Deref(int numDims)
|
|
{
|
|
if (numDims > 1)
|
|
{
|
|
for (int i = 0; i < numDims; i++)
|
|
stackMap.PopStack(CilMain.Where); // index
|
|
var arrayType = (CilType) stackMap.PopStack(CilMain.Where);
|
|
|
|
if (arrayType.ArrayRank == 0)
|
|
{
|
|
Deref(numDims, true);
|
|
arrayType = GenericArrayType;
|
|
}
|
|
else
|
|
{
|
|
Deref(numDims, false);
|
|
arrayType = arrayType.AdjustRank(-numDims + 1);
|
|
}
|
|
|
|
// after recursive de-ref, push a 1-dim array, and index
|
|
stackMap.PushStack(arrayType);
|
|
stackMap.PushStack(JavaType.IntegerType);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Deref(int numDims, bool isGenericArray)
|
|
{
|
|
int localIndex = locals.GetTempIndex(JavaType.IntegerType);
|
|
code.NewInstruction(JavaType.IntegerType.StoreOpcode, null, localIndex);
|
|
|
|
if (numDims > 2)
|
|
Deref(numDims - 1, isGenericArray);
|
|
|
|
if (isGenericArray)
|
|
{
|
|
code.NewInstruction(0xB8 /* invokestatic */,
|
|
SystemArrayType, LoadArrayMethod);
|
|
}
|
|
else
|
|
{
|
|
code.NewInstruction(0x32 /* aaload */, null, null);
|
|
}
|
|
|
|
code.NewInstruction(JavaType.IntegerType.LoadOpcode, null, localIndex);
|
|
locals.FreeTempIndex(localIndex);
|
|
}
|
|
|
|
|
|
|
|
public static bool MaybeGetProxy(CilType fromType, CilType intoType, JavaCode code)
|
|
{
|
|
if ( fromType.IsArray
|
|
|| object.ReferenceEquals(fromType, GenericArrayType)
|
|
|| fromType.Equals(JavaType.StringType))
|
|
{
|
|
if (GenericUtil.ShouldCallGenericCast(fromType, intoType))
|
|
{
|
|
code.NewInstruction(0xB8 /* invokestatic */,
|
|
SystemArrayType, GetProxyMethod);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
public static void InitializeArray(object initialValue, JavaCode code)
|
|
{
|
|
//
|
|
// the input array on the stack should be a primitive array
|
|
//
|
|
|
|
var array = code.StackMap.PopStack(CilMain.Where);
|
|
|
|
int size = 0;
|
|
switch (array.PrimitiveType)
|
|
{
|
|
case TypeCode.Boolean: case TypeCode.SByte: case TypeCode.Byte: size = 1; break;
|
|
case TypeCode.Char: case TypeCode.Int16: case TypeCode.UInt16: size = 2; break;
|
|
case TypeCode.Single: case TypeCode.Int32: case TypeCode.UInt32: size = 4; break;
|
|
case TypeCode.Double: case TypeCode.Int64: case TypeCode.UInt64: size = 8; break;
|
|
default: size = 0; break;
|
|
}
|
|
if (size == 0 || array.ArrayRank == 0)
|
|
throw new Exception("invalid array for InitializeArray");
|
|
|
|
//
|
|
// the initializer byte buffer should be divisible by element size
|
|
//
|
|
|
|
var bytes = initialValue as byte[];
|
|
int count = 0;
|
|
if (bytes != null && bytes.Length != 0 && (bytes.Length % size) == 0)
|
|
count = bytes.Length / size;
|
|
if (count == 0)
|
|
throw new Exception("invalid data for InitializeArray");
|
|
|
|
//
|
|
// prepare the operand stack for as many operands as we will need
|
|
//
|
|
|
|
code.StackMap.PushStack(array);
|
|
code.StackMap.PushStack(JavaType.IntegerType);
|
|
code.StackMap.PushStack(size == 8 ? JavaType.LongType : JavaType.IntegerType);
|
|
|
|
code.StackMap.PopStack(CilMain.Where);
|
|
code.StackMap.PopStack(CilMain.Where);
|
|
code.StackMap.PopStack(CilMain.Where);
|
|
|
|
//
|
|
// generate a sequence of instructions to initialize the array,
|
|
// a reference to which was already pushed at the top of the stack
|
|
//
|
|
|
|
byte storeArrayOpcode =
|
|
((CilType) array).AdjustRank(-array.ArrayRank).StoreArrayOpcode;
|
|
|
|
for (int index = 0; index < count; index++)
|
|
{
|
|
if (index > 0)
|
|
code.NewInstruction(0x59 /* dup */, null, null);
|
|
code.NewInstruction(0x12 /* ldc */, null, (int) index);
|
|
code.NewInstruction(0x12 /* ldc */, null,
|
|
Value(bytes, index * size, array.PrimitiveType));
|
|
code.NewInstruction(storeArrayOpcode, null, null);
|
|
}
|
|
|
|
object Value(byte[] bytes, int offset, TypeCode primitive)
|
|
{
|
|
switch (primitive)
|
|
{
|
|
case TypeCode.Boolean:
|
|
bool boolValue = BitConverter.ToBoolean(bytes, offset);
|
|
return (int) (boolValue ? 1 : 0);
|
|
case TypeCode.SByte:
|
|
return (int) (((sbyte[]) (object) bytes)[offset]);
|
|
case TypeCode.Byte:
|
|
return (int) bytes[offset];
|
|
case TypeCode.Char:
|
|
return (int) BitConverter.ToChar(bytes, offset);
|
|
case TypeCode.Int16:
|
|
return (int) BitConverter.ToInt16(bytes, offset);
|
|
case TypeCode.UInt16:
|
|
return (int) BitConverter.ToUInt16(bytes, offset);
|
|
case TypeCode.Int32:
|
|
return BitConverter.ToInt32(bytes, offset);
|
|
case TypeCode.UInt32:
|
|
return (int) BitConverter.ToUInt32(bytes, offset);
|
|
case TypeCode.Int64:
|
|
return BitConverter.ToInt64(bytes, offset);
|
|
case TypeCode.UInt64:
|
|
return (long) BitConverter.ToUInt64(bytes, offset);
|
|
case TypeCode.Single:
|
|
return BitConverter.ToSingle(bytes, offset);
|
|
case TypeCode.Double:
|
|
return BitConverter.ToDouble(bytes, offset);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static bool CheckCast(CilType castType, bool @throw, JavaCode code)
|
|
{
|
|
if ( object.ReferenceEquals(castType, GenericArrayType)
|
|
|| (castType.IsArray && ( castType.IsInterface
|
|
|| castType.IsGenericParameter
|
|
|| castType.ClassName == JavaType.ObjectType.ClassName
|
|
|| castType.ClassName == CilType.SystemValueType.ClassName)))
|
|
{
|
|
// if casting to Object[], ValueType[], to an array of interface type,
|
|
// or to an array of a generic parameter, we can't rely on a simple
|
|
// 'checkcast' or 'instanceof', because the jvm will permit the cast
|
|
// of a value type array to the aforementioned reference types.
|
|
//
|
|
// instead, we generate a call to system.Array.CheckCast in baselib,
|
|
// except if we are generating code for the system.Array class itself.
|
|
|
|
if (code.Method.Class.Name.StartsWith("system.Array"))
|
|
return false;
|
|
|
|
// note the caller of this method already popped the stack once.
|
|
// which we have to undo that, before we push anything else.
|
|
code.StackMap.PushStack(JavaType.ObjectType); // array
|
|
|
|
var method = new JavaMethodRef("CheckCast", JavaType.ObjectType);
|
|
method.Parameters.Add(new JavaFieldRef("", JavaType.ObjectType));
|
|
|
|
if ( object.ReferenceEquals(castType, GenericArrayType)
|
|
|| castType.IsGenericParameter)
|
|
{
|
|
method.Parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));
|
|
GenericUtil.LoadMaybeGeneric(castType, code);
|
|
// CilType.SystemTypeType pushed to the stack
|
|
}
|
|
else
|
|
{
|
|
method.Parameters.Add(new JavaFieldRef("", JavaType.ClassType));
|
|
code.NewInstruction(0x12 /* ldc */, castType.AdjustRank(-1), null);
|
|
code.StackMap.PushStack(JavaType.ClassType);
|
|
}
|
|
|
|
method.Parameters.Add(new JavaFieldRef("", JavaType.BooleanType));
|
|
code.NewInstruction(0x12 /* ldc */, null, (int) (@throw ? 1 : 0));
|
|
code.StackMap.PushStack(JavaType.IntegerType); // boolean
|
|
|
|
code.NewInstruction(0xB8 /* invokestatic */, SystemArrayType, method);
|
|
|
|
code.StackMap.PopStack(CilMain.Where); // boolean
|
|
code.StackMap.PopStack(CilMain.Where); // class/type
|
|
code.StackMap.PopStack(CilMain.Where); // array
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
static CodeArrays()
|
|
{
|
|
var parameters = new List<JavaFieldRef>(4);
|
|
parameters.Add(new JavaFieldRef("", JavaType.ObjectType));
|
|
parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));
|
|
parameters.Add(new JavaFieldRef("", CilType.SystemValueType));
|
|
parameters.Add(new JavaFieldRef("", JavaType.IntegerType));
|
|
InitArrayMethod = new JavaMethodRef("Initialize", JavaType.VoidType, parameters);
|
|
|
|
parameters = new List<JavaFieldRef>(3);
|
|
parameters.Add(new JavaFieldRef("", JavaType.ObjectType));
|
|
parameters.Add(new JavaFieldRef("", JavaType.IntegerType));
|
|
parameters.Add(new JavaFieldRef("", JavaType.ObjectType));
|
|
StoreArrayMethod = new JavaMethodRef("Store", JavaType.VoidType, parameters);
|
|
|
|
parameters = new List<JavaFieldRef>(2);
|
|
parameters.Add(new JavaFieldRef("", JavaType.ObjectType));
|
|
parameters.Add(new JavaFieldRef("", JavaType.IntegerType));
|
|
LoadArrayMethod = new JavaMethodRef("Load", JavaType.ObjectType, parameters);
|
|
}
|
|
|
|
|
|
|
|
internal static readonly JavaMethodRef InitArrayMethod;
|
|
internal static readonly JavaMethodRef StoreArrayMethod;
|
|
internal static readonly JavaMethodRef LoadArrayMethod;
|
|
|
|
internal static readonly CilType SystemArrayType =
|
|
CilType.From(new JavaType(0, 0, "system.Array"));
|
|
|
|
internal static readonly JavaMethodRef GetProxyMethod =
|
|
new JavaMethodRef("GetProxy", SystemArrayType, JavaType.ObjectType);
|
|
|
|
// the following type is a plain instance of java.lang.Object, but it is compared
|
|
// by reference in several places, to identify a generic array, i.e. T[].
|
|
internal static readonly CilType GenericArrayType = CilType.From(JavaType.ObjectType);
|
|
}
|
|
|
|
}
|
|
|