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); }
override public object CreateFromString(string input) { if (BoxedType != null) { return(BoxInstance(BoxedType.CreateFromString(input))); } if (CreateFromStringMethod != null) { return(this.CreateFromStringMethod(input)); } else if (_enumValues != null) { int value = 0; string[] valueParts = input.Split(','); foreach (string valuePart in valueParts) { object partValue; int enumFieldValue = 0; try { if (_enumValues.TryGetValue(valuePart.Trim(), out partValue)) { enumFieldValue = global::System.Convert.ToInt32(partValue); } else { try { enumFieldValue = global::System.Convert.ToInt32(valuePart.Trim()); } catch (global::System.FormatException) { foreach (string key in _enumValues.Keys) { if (string.Compare(valuePart.Trim(), key, global::System.StringComparison.OrdinalIgnoreCase) == 0) { if (_enumValues.TryGetValue(key.Trim(), out partValue)) { enumFieldValue = global::System.Convert.ToInt32(partValue); break; } } } } } value |= enumFieldValue; } catch (global::System.FormatException) { throw new global::System.ArgumentException(input, FullName); } } return(value); } throw new global::System.ArgumentException(input, FullName); }
public static CilType CastMaybeGeneric(CilType castType, bool valueOnly, JavaCode code) { if (!castType.IsGenericParameter) { return(castType); } if (!valueOnly) { GenericUtil.ValueLoad(code); } var genericMark = CilMain.GenericStack.Mark(); var(resolvedType, resolvedIndex) = CilMain.GenericStack.Resolve(castType.JavaName); if (resolvedIndex == 0) { if (valueOnly) { // this flag is set when called from LoadFieldAddress. we can cast // the generic field to the actual type only if it is a value type if (resolvedType.IsValueClass) { castType = resolvedType; code.NewInstruction(0xC0 /* checkcast */, castType.AsWritableClass, null); } else if (castType.IsByReference) { var boxedType = new BoxedType(resolvedType, false); code.NewInstruction(0xC0 /* checkcast */, boxedType, null); castType = boxedType; } } else { // this flag is clear whe called from LoadFieldValue and // PushMethodReturnType. we can cast to any known actual types. var arrayRank = castType.GetMethodGenericParameter()?.ArrayRank ?? 0; castType = (arrayRank == 0) ? resolvedType : resolvedType.AdjustRank(arrayRank); if (!castType.IsReference) { var boxedType = new BoxedType(castType, false); code.NewInstruction(0xC0 /* checkcast */, boxedType, null); boxedType.GetValue(code); } else { code.NewInstruction(0xC0 /* checkcast */, castType.AsWritableClass, null); } } } CilMain.GenericStack.Release(genericMark); return(castType); }
public void Address(CilType arrayType) { 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 (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 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); } }
void UnboxObject(Code cilOp, object data) { if (data is TypeReference typeRef) { var dataType = CilType.From(typeRef); if (dataType.IsValueClass) { if (dataType.IsGenericParameter) { if (cilOp == Code.Unbox_Any) { // unboxing (casting) a concrete type as a generic type var stackTop = (CilType)code.StackMap.PopStack(CilMain.Where); if (stackTop.Equals(JavaType.ObjectType) || dataType.Equals(stackTop)) { code.NewInstruction(0x00 /* nop */, null, null); } else { code.NewInstruction(0xC0 /* checkcast */, dataType, null); } code.StackMap.PushStack(dataType); } else // plain Unbox { throw new InvalidProgramException(); } } else { code.StackMap.PopStack(CilMain.Where); code.NewInstruction(0xC0 /* checkcast */, dataType, null); code.StackMap.PushStack(dataType); } } else if (!dataType.IsReference) { var boxedType = new BoxedType(dataType, false); var fromType = code.StackMap.PopStack(CilMain.Where); if (!fromType.Equals(boxedType)) { code.NewInstruction(0xC0 /* checkcast */, boxedType, null); } boxedType.GetValue(code); code.StackMap.PushStack(dataType); } else { if (cilOp == Code.Unbox_Any) { NullCheck(); CastToClass(data); } else // plain Unbox { throw new InvalidProgramException(); } } } }
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); } }
bool ImportParameters(MethodReference fromMethod) { bool appendSuffix = false; int n = fromMethod.HasParameters ? fromMethod.Parameters.Count : 0; Parameters = new List <JavaFieldRef>(n); for (int i = 0; i < n; i++) { var fromParameterType = fromMethod.Parameters[i].ParameterType; var paramType = CilType.From(fromParameterType); if (paramType.IsPointer) { if (paramType.IsValueClass || (!paramType.IsReference)) { paramType = CilType.MakeSpanOf(paramType); } else { throw CilMain.Where.Exception("invalid pointer type in parameter"); } } if (paramType.IsGenericParameter) { appendSuffix = true; var nm = "-generic-"; if (paramType.IsArray) { nm += "array-" + paramType.ArrayRank + "-"; } if (paramType.IsByReference) { nm = "-ref" + nm.Replace("&", ""); } nm += "$" + ((GenericParameter)fromParameterType.GetElementType()).Position; paramType = CilType.WrapMethodGenericParameter(paramType, nm); Flags |= GEN_ARGS; } if (paramType.IsByReference) { if (paramType.IsValueClass) { paramType = paramType.MakeByRef(); appendSuffix = true; } else { // byref of any reference type parameter becomes system.Reference, // so add a unique method name suffix var paramBoxedType = new BoxedType(paramType, false); paramType = paramBoxedType; appendSuffix |= paramBoxedType.IsBoxedReference; } } if (!appendSuffix) { appendSuffix |= fromParameterType.IsGenericInstance; if (!paramType.IsReference) { appendSuffix |= ShouldRenamePrimitive(paramType); } } Parameters.Add(new JavaFieldRef("", paramType)); } var returnType = CilType.From(fromMethod.ReturnType); if (returnType.IsGenericParameter) { returnType = CilType.WrapMethodGenericParameter(returnType); } else if (returnType.IsByReference) { returnType = new BoxedType(returnType, false); } ReturnType = returnType; appendSuffix |= fromMethod.ReturnType.IsGenericInstance; return(appendSuffix);
int InitLocalsVars(MethodBody cilBody, List <CilType> localTypes, int nextIndex) { int highestIndex = -1; if (cilBody.HasVariables) { foreach (var cilVar in cilBody.Variables) { if (cilVar.Index > highestIndex) { highestIndex = cilVar.Index; } } } varToLocalMap = new int[highestIndex + 1]; if (cilBody.HasVariables) { foreach (var cilVar in cilBody.Variables) { CilMain.Where.Push("local #" + cilVar.Index); var genericMark = CilMain.GenericStack.Mark(); var varType = CilMain.GenericStack.EnterType(cilVar.VariableType); if (varType.IsValueClass || varType.IsPointer) { bool isByReference = varType.IsByReference; if (varType.IsPointer) { varType = CilType.MakeSpanOf(varType); } // value classes are allocated at the top of the method if (!isByReference) { ValueUtil.InitLocal(varType, nextIndex, code); varType = varType.MakeClonedAtTop(); } } else if (varType.IsByReference) { varType = new BoxedType(varType, false); } else if (varType.IsArray && varType.IsGenericParameter) { // note that GenericArrayType is compared by reference // in CodeArrays, to detect an array of a generic type T[] varType = CodeArrays.GenericArrayType; } varToLocalMap[cilVar.Index] = nextIndex; nextIndex += varType.Category; localTypes.Add(varType); while (nextIndex > localTypes.Count) { localTypes.Add(null); } CilMain.GenericStack.Release(genericMark); CilMain.Where.Pop(); } } return(nextIndex); }
public static void BuildGenericProxy2(CilInterfaceMethod ifcMethod, CilMethod targetMethod, bool parentField, CilType intoType, JavaClass ifcClass) { // // create proxy method // var targetMethod2 = targetMethod.WithGenericParameters; var ifcMethod2 = ifcMethod.Method.WithGenericParameters; var newMethod = new JavaMethod(ifcClass, targetMethod2); newMethod.Name = ifcMethod2.Name; newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE; var code = newMethod.Code = new JavaCode(); code.Method = newMethod; code.Instructions = new List <JavaCode.Instruction>(); // // push a reference to the parent object // code.NewInstruction(0x19 /* aload */, null, (int)0); if (parentField) { code.NewInstruction(0xB4 /* getfield */, new JavaType(0, 0, ifcClass.Name), new JavaFieldRef(ParentFieldName, intoType)); } // // push all other parameters // int numArgs = newMethod.Parameters.Count; int index = 1; int maxStack = 1; for (int i = 0; i < numArgs; i++) { var ifcArg = ifcMethod2.Parameters[i].Type; code.NewInstruction(ifcArg.LoadOpcode, null, (int)index); index += ifcArg.Category; var clsArg = (CilType)targetMethod2.Parameters[i].Type; if (JavaType.ObjectType.Equals(ifcArg)) { if (!clsArg.IsReference) { var boxedArg = new BoxedType(clsArg, false); code.NewInstruction(0xC0 /* checkcast */, boxedArg, null); boxedArg.GetValue(code); } else if (!JavaType.ObjectType.Equals(clsArg)) { code.NewInstruction(0xC0 /* checkcast */, clsArg, null); } // a parameter in the target method may be a concrete type, // but if it is a generic java.lang.Object in the interface, // then it must be a generic java.lang.Object in the proxy newMethod.Parameters[i] = new JavaFieldRef("", ifcArg); } maxStack += clsArg.Category; } // // invoke proxy target method // code.NewInstruction(0xB6 /* invokevirtual */, intoType, targetMethod2); // // return value from method // var clsRet = (CilType)targetMethod2.ReturnType; var ifcRet = ifcMethod2.ReturnType; if (JavaType.ObjectType.Equals(ifcRet)) { if (!clsRet.IsReference) { var boxedArg = new BoxedType(clsRet, false); boxedArg.BoxValue(code); } // the return value in the target method may be a concrete type, // but if it is a generic java.lang.Object in the interface, // then it must also be a generic java.lang.Object in the proxy newMethod.ReturnType = ifcRet; code.NewInstruction(ifcRet.ReturnOpcode, null, null); } else { code.NewInstruction(clsRet.ReturnOpcode, null, null); } code.MaxLocals = index; code.MaxStack = maxStack; ifcClass.Methods.Add(newMethod); }
void InitLocalsRefs(MethodDefinition cilMethod, List <CilType> localTypes) { // if the method includes instructions that take the address of // a local (argument/variable), then: // // if it is a primitive value type, or a reference, then we have // to box that into a system.<primitive> or system.Reference. // // if it is a class value type, or a generic parameter, and if // it was not passed by reference, then we have to make a copy // of the incoming value. // // a pointer argument is actually passed as a span (value type) // so if it is modified, we have to make a copy, as above. // // (if it was a generic parameter which represents a value type, // then a copy was already made when the value was loaded.) var processed = new bool[localTypes.Count]; foreach (var inst in cilMethod.Body.Instructions) { int index = -1; bool arg = false; bool isPointerSpan = false; var instOpc = inst.OpCode.Code; if (instOpc == Code.Ldarga || instOpc == Code.Ldarga_S) { if (inst.Operand is ParameterDefinition param) { index = ArgumentIndex(param.Sequence); arg = true; } } else if (instOpc == Code.Starg || instOpc == Code.Starg_S) { if (inst.Operand is ParameterDefinition param) { var paramType = param.ParameterType; if ((paramType.IsValueType && (!paramType.IsPrimitive)) || (isPointerSpan = paramType.IsPointer)) { index = ArgumentIndex(param.Sequence); arg = true; } } } else if (instOpc == Code.Ldloca || instOpc == Code.Ldloca_S) { if (inst.Operand is VariableDefinition var) { index = VariableIndex(var.Index); arg = false; } } // // check if already processed, or does not require processing // if (index == -1 || processed[index]) { continue; } processed[index] = true; var plainType = localTypes[index]; if (plainType.IsByReference) { continue; } // // copy class value type argument, or generic parameter, if // it was indeed passed by value, because we know the method // is going to modify the value. (this also applies to a // pointer, which is passed as a span, a value type object.) // if (plainType.IsValueClass || plainType.IsGenericParameter || isPointerSpan) { if (arg) { InitLocalsRefs2(plainType, null, true, index); if (plainType.IsGenericParameter || isPointerSpan) { // must not set the stackmap for a generic parameter, // see InitLocalsArgs for the difference between the // stackmap and the localTypes array localTypes[index] = plainType.MakeClonedAtTop(); } else { var byrefType = plainType.MakeClonedAtTop().MakeByRef(); localTypes[index] = byrefType; stackMap.SetLocal(index, byrefType); } } } else { // // otherwise the method is going to load the address of a local // (either argument or variable) which is a primitive value, or // a reference, so we have to box that local // // note that boxedType will be marked as if MakeClonedAtTop() var boxedType = new BoxedType(plainType, true); localTypes[index] = boxedType; stackMap.SetLocal(index, boxedType); InitLocalsRefs2(plainType, boxedType, arg, index); } } }
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); }
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)); } }