void StoreObject(object data) { if (data is TypeReference typeRef) { var dataType = CilType.From(typeRef); var valueType = (CilType)code.StackMap.PopStack(CilMain.Where); var intoType = (CilType)code.StackMap.PopStack(CilMain.Where); if (CodeSpan.LoadStore(false, intoType, null, dataType, code)) { return; } if ((!dataType.IsReference) && intoType is BoxedType intoBoxedType && dataType.PrimitiveType == intoBoxedType.UnboxedType.PrimitiveType) { // 'stobj primitive' with a primitive value on the stack intoBoxedType.SetValueOV(code); return; } code.StackMap.PushStack(intoType); code.StackMap.PushStack(valueType); GenericUtil.ValueCopy(dataType, code, true); code.StackMap.PopStack(CilMain.Where); code.StackMap.PopStack(CilMain.Where); } else { throw new InvalidProgramException(); } }
void Store(CilType elemType) { 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); } if (arrayType.IsValueClass || elemType.IsValueClass) { CilMethod.ValueMethod(CilMethod.ValueClone, code); } code.NewInstruction(elemType.StoreArrayOpcode, null, null); } }
public static void Indirection(JavaCode code, Code cilOp) { var(name, opcodeType) = IndirectOpCodeToNameAndType(cilOp); bool isRef = opcodeType.Equals(JavaType.ObjectType); bool isLoad; if (cilOp >= Code.Ldind_I1 && cilOp <= Code.Ldind_Ref) { isLoad = true; } else { isLoad = false; var valueType = code.StackMap.PopStack(CilMain.Where); if (valueType.IsReference != isRef) { throw new InvalidProgramException(); } } var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where); if (stackTop.IsGenericParameter) { if (isLoad) { var resultType = GenericUtil.CastMaybeGeneric(stackTop, false, code); if (resultType == stackTop && (!stackTop.Equals(JavaType.ObjectType))) { code.NewInstruction(0xC0 /* checkcast */, stackTop.AsWritableClass, null); resultType = stackTop; } code.StackMap.PushStack(resultType); } else { code.NewInstruction(0x5F /* swap */, null, null); GenericUtil.ValueCopy(stackTop, code); } return; } // // non-generic object reference // var boxedType = stackTop as BoxedType; if (boxedType == null || boxedType.IsBoxedReference != isRef) { if (CodeSpan.LoadStore(isLoad, stackTop, opcodeType, null, code)) { return; } if (object.ReferenceEquals(stackTop, CodeArrays.GenericArrayType)) { // a byref parameter T[] gets translated to java.lang.Object, // so we have to explicitly cast it to system.Reference boxedType = new BoxedType(stackTop, false); code.NewInstruction(0x5F /* swap */, null, null); code.NewInstruction(0xC0 /* checkcast */, boxedType.AsWritableClass, null); code.NewInstruction(0x5F /* swap */, null, null); } else { throw new ArgumentException($"incompatible type '{stackTop}'"); } } var unboxedType = boxedType.UnboxedType; var unboxedTypeCode = unboxedType.IsReference ? 0 : unboxedType.PrimitiveType; JavaMethodRef method; if (CompareIndirectTypes(unboxedTypeCode, opcodeType.PrimitiveType)) { // indirect access to a primitive or reference type, with a // reference type that represents the boxed form of the same type. if (isLoad) { boxedType.GetValue(code); if (unboxedType.IsReference) { // if we know the type of indirected value, cast to it if (!unboxedType.Equals(JavaType.ObjectType)) { code.NewInstruction(0xC0 /* checkcast */, unboxedType.AsWritableClass, null); } } else { unboxedType = CilType.From(opcodeType); } code.StackMap.PushStack(unboxedType); } else { // if we are storing a real array into a boxed reference of // e.g., system.Array, then we have to create an array proxy CodeArrays.MaybeGetProxy(CodeArrays.GenericArrayType, unboxedType, code); boxedType.SetValueOV(code); } return; } // indirect access to a primitive value from a reference type that // represents some other a primitive value; for example ldind.r4 // from a system.Int32. we call the "CodeNumber.Indirection methods" // helpers, defined in all baselib primitives, to assist. if (opcodeType.IsIntLike) { opcodeType = JavaType.IntegerType; } if (isLoad) { method = new JavaMethodRef("Get_" + name, opcodeType); code.StackMap.PushStack(CilType.From(opcodeType)); } else { method = new JavaMethodRef("Set_" + name, JavaType.VoidType, opcodeType); } code.NewInstruction(0xB6 /* invokevirtual */, boxedType, method); }
bool StoreFieldValue(string fldName, CilType fldType, CilType fldClass, bool isStatic, bool isVolatile) { // // we generate a sequence of instructions based on the combination of // isStatic, isGeneric, isBoxed, isCopyable // // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0: // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield // // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X: // [VAL] -> getstatic -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1: // [VAL] -> getstatic -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0: // [VAL] -> putstatic // // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X: // * [INS] [VAL] -> swap -> [VAL] [INS] -> getfield -> [VAL] [BOX] -> BoxedType.SetOV // // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1: // [INS] [VAL] -> swap -> [VAL] [INS] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0: // [INS] [VAL] -> putfield // // the combinations marked with an asterisk require swapping operands, // which is handled more effectively by first popping the value operand // var fldRef = new JavaFieldRef(fldName, fldType); bool isBoxed = fldType is BoxedType; if (isStatic) { if (fldClass.HasGenericParameters) { // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1 // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0 StoreStaticGeneric(fldClass, fldType, fldRef, isVolatile); } else { // isStatic=1 IsGeneric=0 IsBoxed=1 IsCopyable=X // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=1 // isStatic=1 IsGeneric=0 IsBoxed=0 IsCopyable=0 StoreStaticRegular(fldClass, fldType, fldRef, isVolatile); } } else { // isStatic=0 IsGeneric=X IsBoxed=1 IsCopyable=X // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=1 // isStatic=0 IsGeneric=X IsBoxed=0 IsCopyable=0 StoreInstance(fldClass, fldType, fldRef, isVolatile); } return(true); void StoreStaticGeneric(CilType fldClass, CilType fldType, JavaFieldRef fldRef, bool isVolatile) { // isStatic=1 IsGeneric=1 IsBoxed=1 IsCopyable=X: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [BOX] -> BoxedType.SetVO // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=1: // [VAL] -> LoadST -> [VAL] [ST] -> getfield -> [VAL] [OBJ] -> ValueType.Copy // // isStatic=1 IsGeneric=1 IsBoxed=0 IsCopyable=0: // * [VAL] -> LoadST -> [VAL] [ST] -> swap -> [ST] [VAL] -> putfield if (fldType.IsValueClass) { fldClass = LoadStaticData(fldClass); code.NewInstruction(0xB4 /* getfield */, fldClass.AsWritableClass, fldRef); if (fldType is BoxedType boxedType && (!boxedType.IsBoxedIntPtr)) { boxedType.SetValueVO(code, isVolatile); } else { GenericUtil.ValueCopy(fldType, code); } }
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); } }