Exemplo n.º 1
0
        void InitLocalsRefs2(CilType plainType, BoxedType boxedType, bool arg, int index)
        {
            if (arg)
            {
                code.NewInstruction(plainType.LoadOpcode, null, index);
            }
            else
            {
                code.NewInstruction(plainType.InitOpcode, null, null);
            }

            stackMap.PushStack(plainType);

            if (boxedType != null)
            {
                boxedType.BoxValue(code);
            }
            else if (plainType.IsGenericParameter)
            {
                GenericUtil.ValueClone(code);
            }
            else
            {
                CilMethod.ValueMethod(CilMethod.ValueClone, code);
                code.NewInstruction(0xC0 /* checkcast */, plainType, null);
            }

            code.NewInstruction(0x3A /* astore */, null, index);
            stackMap.PopStack(CilMain.Where);
        }
Exemplo n.º 2
0
        public static void ValueCopy(CilType valueType, JavaCode code, bool swap = false)
        {
            // if 'from' value is pushed before 'into' object, call with swap == false
            // if 'into' object is pushed before 'from' value, call with swap == true
            if (valueType.IsGenericParameter)
            {
                if (swap)
                {
                    code.NewInstruction(0x5F /* swap */, null, null);
                }
                else
                {
                    // if storing a primitive value into a generic type,
                    // and the generic type can be resolved to a primitive type,
                    // then use a boxed-set method call

                    var stackArray    = code.StackMap.StackArray();
                    int stackArrayLen = stackArray.Length;
                    if (stackArrayLen > 0 && (!stackArray[stackArrayLen - 1].IsReference))
                    {
                        var genericMark = CilMain.GenericStack.Mark();
                        var(primitiveType, _) = CilMain.GenericStack.Resolve(valueType.JavaName);
                        CilMain.GenericStack.Release(genericMark);

                        if (!primitiveType.IsReference)
                        {
                            var boxedType = new BoxedType(primitiveType, false);
                            code.NewInstruction(0xC0 /* checkcast */, boxedType, null);
                            boxedType.SetValueVO(code);
                            return;
                        }
                    }
                }

                code.NewInstruction(0xB8 /* invokestatic */, SystemGenericType,
                                    new JavaMethod("Copy", JavaType.VoidType,
                                                   JavaType.ObjectType, JavaType.ObjectType));
            }
            else
            {
                if (swap)
                {
                    code.NewInstruction(0x5F /* swap */, null, null);
                }
                CilMethod.ValueMethod(CilMethod.ValueCopyTo, code);
            }
        }
Exemplo n.º 3
0
        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 (arrayType.IsValueClass || elemType.IsValueClass)
                {
                    CilMethod.ValueMethod(CilMethod.ValueClone, code);
                }
            }

            stackMap.PushStack(elemType);
        }
Exemplo n.º 4
0
        void LoadObject(Code cilOp, object data)
        {
            if (data is TypeReference typeRef)
            {
                var dataType = CilType.From(typeRef);
                var fromType = (CilType)code.StackMap.PopStack(CilMain.Where);

                if (CodeSpan.LoadStore(true, fromType, null, dataType, code))
                {
                    return;
                }

                if ((!dataType.IsReference) && cilOp == Code.Ldobj &&
                    fromType is BoxedType fromBoxedType &&
                    dataType.PrimitiveType == fromBoxedType.UnboxedType.PrimitiveType)
                {
                    // 'ldobj primitive' with a corresponding boxed type on the stack.
                    // we implement by unboxing the boxed type into a primitive value.
                    fromBoxedType.GetValue(code);
                    stackMap.PushStack(fromBoxedType.UnboxedType);
                    return;
                }

                if (dataType.IsGenericParameter ||
                    (dataType.IsValueClass && dataType.Equals(fromType)))
                {
                    code.StackMap.PushStack(dataType);

                    if (SkipClone(cilInst.Next, fromType))
                    {
                        // see below for the several cases where we determine
                        // that we can safely avoid making a clone of the value
                        code.NewInstruction(0x00 /* nop */, null, null);
                    }
                    else if (dataType.IsGenericParameter)
                    {
                        GenericUtil.ValueClone(code);
                    }
                    else if (cilOp == Code.Ldobj)
                    {
                        code.NewInstruction(0x00 /* nop */, null, null);
                    }
                    else
                    {
                        CilMethod.ValueMethod(CilMethod.ValueClone, code);
                    }
                    return;
                }

                if (dataType.IsReference && cilOp == Code.Box)
                {
                    // 'box' is permitted on reference types, we treat it as a cast
                    code.StackMap.PushStack(fromType);
                    CastToClass(data);
                    return;
                }

                if (!dataType.IsReference)
                {
                    var boxedType = new BoxedType(dataType, false);
                    code.StackMap.PushStack(boxedType);
                    boxedType.BoxValue(code);
                    return;
                }
            }
            throw new InvalidProgramException();


            bool SkipClone(Mono.Cecil.Cil.Instruction next, CilType checkType)
            {
                if (checkType.IsByReference)
                {
                    return(true);
                }

                if (next == null)
                {
                    return(false);
                }
                var op = next.OpCode.Code;

                if (IsBrTrueBrFalseIsInst(op))
                {
                    // if 'ldobj' or 'box' is followed by a check for null,
                    // we don't actually need to clone just for the test
                    return(true);
                }

                if (op == Code.Box)
                {
                    if (next.Operand is TypeReference nextTypeRef)
                    {
                        // 'ldobj' may be followed by 'box', to load the value
                        // of a byref value type, and then box it into an object.
                        // effectively we only need to clone once, in such a case.
                        return(CilType.From(nextTypeRef).Equals(checkType));
                    }
                }

                var(storeType, _) = locals.GetLocalFromStoreInst(op, next.Operand);
                if (storeType != null)
                {
                    // 'ldobj' or 'box' may be followed by a store instruction.
                    // if storing into a variable of the same value type, the
                    // next instruction will copy the value, so skip clone.
                    return(storeType.Equals(checkType));
                }

                if (op == Code.Ret && checkType.IsClonedAtTop)
                {
                    // if the value on the stack was cloned/boxed at the top of
                    // the method, then we can avoid clone and return it directly.
                    // see also:  CilType.MakeClonedAtTop and its callers.
                    return(true);
                }

                if (op == Code.Unbox_Any)
                {
                    if (next.Operand is TypeReference nextTypeRef)
                    {
                        // 'ldobj' or 'box' may be followed by 'unbox' and
                        // then one of the instructions above, e.g. 'brtrue'
                        // or 'stloc'.  we still want to detect such a case
                        // and prevent a needless clone.
                        return(SkipClone(next.Next, CilType.From(nextTypeRef)));
                    }
                }

                return(false);
            }
        }
Exemplo n.º 5
0
        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);
            }
        }
Exemplo n.º 6
0
        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);
            }
        }
Exemplo n.º 7
0
        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);
        }