public static void WriteTo(this Instruction instruction, ITextOutput writer, Func <OpCode, string> getOpCodeDocumentation) { writer.WriteDefinition(DnlibExtensions.OffsetToString(instruction.GetOffset()), instruction, TextTokenType.Label, false); writer.Write(':', TextTokenType.Operator); writer.WriteSpace(); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode, TextTokenType.OpCode); if (instruction.Operand != null) { writer.WriteSpace(); if (instruction.OpCode == OpCodes.Ldtoken) { var member = instruction.Operand as IMemberRef; if (member != null && member.IsMethod) { writer.Write("method", TextTokenType.Keyword); writer.WriteSpace(); } else if (member != null && member.IsField) { writer.Write("field", TextTokenType.Keyword); writer.WriteSpace(); } } WriteOperand(writer, instruction.Operand); } if (getOpCodeDocumentation != null) { var doc = getOpCodeDocumentation(instruction.OpCode); if (doc != null) { writer.Write("\t", TextTokenType.Text); writer.Write("// " + doc, TextTokenType.Comment); } } }
void ConvertParameters(List <ByteCode> body) { ILVariable thisParameter = null; if (methodDef.HasThis) { TypeDef type = methodDef.DeclaringType; thisParameter = new ILVariable(); thisParameter.Type = DnlibExtensions.IsValueType(type) ? new ByRefSig(type.ToTypeSig()) : type.ToTypeSig(); thisParameter.Name = "this"; thisParameter.OriginalParameter = methodDef.Parameters[0]; } foreach (Parameter p in methodDef.Parameters.SkipNonNormal()) { this.Parameters.Add(new ILVariable { Type = p.Type, Name = p.Name, OriginalParameter = p }); } if (this.Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { // last parameter must be 'value', so rename it this.Parameters.Last().Name = "value"; } foreach (ByteCode byteCode in body) { Parameter p; switch (byteCode.Code) { case ILCode.Ldarg: p = byteCode.Operand as Parameter; byteCode.Code = ILCode.Ldloc; byteCode.Operand = p == null ? null : p.IsHiddenThisParameter ? thisParameter : this.Parameters[p.MethodSigIndex]; break; case ILCode.Starg: p = byteCode.Operand as Parameter; byteCode.Code = ILCode.Stloc; byteCode.Operand = p == null ? null : p.IsHiddenThisParameter ? thisParameter : this.Parameters[p.MethodSigIndex]; break; case ILCode.Ldarga: p = byteCode.Operand as Parameter; byteCode.Code = ILCode.Ldloca; byteCode.Operand = p == null ? null : p.IsHiddenThisParameter ? thisParameter : this.Parameters[p.MethodSigIndex]; break; } } if (thisParameter != null) { this.Parameters.Add(thisParameter); } }
public static void WriteTo(this Instruction instruction, MethodDef method, ITextOutput writer) { writer.WriteDefinition(DnlibExtensions.OffsetToString(instruction.Offset), instruction); writer.Write(": "); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); if (instruction.Operand != null) { writer.Write(' '); if (instruction.OpCode == OpCodes.Ldtoken) { MemberRef member = instruction.Operand as MemberRef; if ((member != null && member.IsMethodRef) || instruction.Operand is MethodDef || instruction.Operand is MethodSpec) { writer.Write("method "); } else if ((member != null && member.IsFieldRef) || instruction.Operand is FieldDef) { writer.Write("field "); } } WriteOperand(writer, instruction.Operand.ResolveGenericParams(method)); } }
/// <summary> /// Handles both object and collection initializers. /// </summary> bool TransformObjectInitializers(ILBlockBase block, List <ILNode> body, ILExpression expr, int pos) { if (!context.Settings.ObjectOrCollectionInitializers) { return(false); } Debug.Assert(body[pos] == expr); // should be called for top-level expressions only ILVariable v; ILExpression newObjExpr; ITypeDefOrRef newObjType; bool isValueType; IMethod ctor; List <ILExpression> ctorArgs; if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) { if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { // v = newObj(ctor, ctorArgs) newObjType = ctor.DeclaringType; isValueType = false; } else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) { // v = defaultvalue(type) isValueType = true; } else { return(false); } } else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) { // call(SomeStruct::.ctor, ldloca(v), remainingArgs) if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) { isValueType = true; newObjType = ctor.DeclaringType; ctorArgs = new List <ILExpression>(ctorArgs); var old = ctorArgs[0]; ctorArgs.RemoveAt(0); newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs); old.AddSelfAndChildrenRecursiveILRanges(newObjExpr.ILRanges); } else { return(false); } } else { return(false); } if (DnlibExtensions.IsValueType(newObjType) != isValueType) { return(false); } int originalPos = pos; // don't use object initializer syntax for closures if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule())) { return(false); } ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType.ToTypeSig()), isValueType); if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements { return(false); } int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1); // Verify that we can inline 'v' into the next instruction: if (pos >= body.Count) { return(false); // reached end of block, but there should be another instruction which consumes the initialized object } var inlining = GetILInlining(method); if (isValueType) { // one ldloc for the use of the initialized object if (inlining.numLdloc.GetOrDefault(v) != 1) { return(false); } // one ldloca for each initializer argument, and also for the ctor call (if it exists) if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0)) { return(false); } // one stloc for the initial store (if no ctor call was used) if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1)) { return(false); } } else { // one ldloc for each initializer argument, and another ldloc for the use of the initialized object if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1) { return(false); } if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0)) { return(false); } } ILExpression nextExpr = body[pos] as ILExpression; if (!inlining.CanInlineInto(nextExpr, v, initializer)) { return(false); } if (expr.Code == ILCode.Stloc) { expr.Arguments[0].AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); expr.Arguments[0] = initializer; } else { Debug.Assert(expr.Code == ILCode.Call); expr.Code = ILCode.Stloc; expr.Operand = v; foreach (var arg in expr.Arguments) { arg.AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); } expr.Arguments.Clear(); expr.Arguments.Add(initializer); } // remove all the instructions that were pulled into the initializer for (int i = originalPos + 1; i < pos; i++) { body[i].AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); } body.RemoveRange(originalPos + 1, pos - originalPos - 1); // now that we know that it's an object initializer, change all the first arguments to 'InitializedObject' ChangeFirstArgumentToInitializedObject(initializer); inlining = GetILInlining(method); inlining.InlineIfPossible(block, body, ref originalPos); return(true); }
void HandleInstanceFieldInitializers(IEnumerable <AstNode> members) { var instanceCtors = members.OfType <ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray(); if (instanceCtorsNotChainingWithThis.Length > 0) { MethodDef ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation <MethodDef>(); if (ctorMethodDef != null && DnlibExtensions.IsValueType(ctorMethodDef.DeclaringType)) { return; } // Recognize field initializers: // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. bool allSame; do { Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault()); if (!m.Success) { break; } FieldDef fieldDef = m.Get <AstNode>("fieldAccess").Single().Annotation <IField>().ResolveFieldWithinSameModule(); if (fieldDef == null) { break; } AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation <FieldDef>() == fieldDef); if (fieldOrEventDecl == null) { break; } Expression initializer = m.Get <Expression>("initializer").Single(); // 'this'/'base' cannot be used in field initializers if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression)) { break; } allSame = true; for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) { if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault())) { allSame = false; break; } } if (allSame) { var ctorIlRanges = new List <Tuple <MemberMapping, List <ILRange> > >(instanceCtorsNotChainingWithThis.Length); for (int i = 0; i < instanceCtorsNotChainingWithThis.Length; i++) { var ctor = instanceCtorsNotChainingWithThis[i]; var stmt = ctor.Body.First(); stmt.Remove(); var mm = ctor.Annotation <MemberMapping>() ?? ctor.Body.Annotation <MemberMapping>(); Debug.Assert(mm != null); if (mm != null) { ctorIlRanges.Add(Tuple.Create(mm, stmt.GetAllRecursiveILRanges())); } } var varInit = fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single(); initializer.Remove(); initializer.RemoveAllILRangesRecursive(); varInit.Initializer = initializer; fieldOrEventDecl.AddAnnotation(ctorIlRanges); } } while (allSame); } }
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) { writer.WriteReference(DnlibExtensions.OffsetToString(instruction.Offset), instruction); }
public static void WriteTo(this Instruction instruction, ITextOutput writer, DisassemblerOptions options, uint baseRva, long baseOffs, IInstructionBytesReader byteReader, MethodDef method) { if (options != null && (options.ShowTokenAndRvaComments || options.ShowILBytes)) { writer.Write("/* ", TextTokenType.Comment); bool needSpace = false; if (options.ShowTokenAndRvaComments) { ulong fileOffset = (ulong)baseOffs + instruction.Offset; writer.WriteReference(string.Format("0x{0:X8}", fileOffset), new AddressReference(options.OwnerModule == null ? null : options.OwnerModule.Location, false, fileOffset, (ulong)instruction.GetSize()), TextTokenType.Comment, false); needSpace = true; } if (options.ShowILBytes) { if (needSpace) { writer.Write(' ', TextTokenType.Comment); } if (byteReader == null) { writer.Write("??", TextTokenType.Comment); } else { int size = instruction.GetSize(); for (int i = 0; i < size; i++) { var b = byteReader.ReadByte(); if (b < 0) { writer.Write("??", TextTokenType.Comment); } else { writer.Write(string.Format("{0:X2}", b), TextTokenType.Comment); } } // Most instructions should be at most 5 bytes in length, but use 6 since // ldftn/ldvirtftn are 6 bytes long. The longest instructions are those with // 8 byte operands, ldc.i8 and ldc.r8: 9 bytes. const int MIN_BYTES = 6; for (int i = size; i < MIN_BYTES; i++) { writer.Write(" ", TextTokenType.Comment); } } } writer.Write(" */", TextTokenType.Comment); writer.WriteSpace(); } writer.WriteDefinition(DnlibExtensions.OffsetToString(instruction.GetOffset()), new InstructionReference(method, instruction), TextTokenType.Label, false); writer.Write(':', TextTokenType.Operator); writer.WriteSpace(); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode, TextTokenType.OpCode); if (instruction.Operand != null) { int count = OPERAND_ALIGNMENT - instruction.OpCode.Name.Length; if (count <= 0) { count = 1; } writer.Write(spaces[count], TextTokenType.Text); if (instruction.OpCode == OpCodes.Ldtoken) { var member = instruction.Operand as IMemberRef; if (member != null && member.IsMethod) { writer.Write("method", TextTokenType.Keyword); writer.WriteSpace(); } else if (member != null && member.IsField) { writer.Write("field", TextTokenType.Keyword); writer.WriteSpace(); } } WriteOperand(writer, instruction.Operand, method); } if (options != null && options.GetOpCodeDocumentation != null) { var doc = options.GetOpCodeDocumentation(instruction.OpCode); if (doc != null) { writer.Write("\t", TextTokenType.Text); writer.Write("// " + doc, TextTokenType.Comment); } } }
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction, MethodDef method, TextTokenType tokenType = TextTokenType.Label) { var r = instruction == null ? null : method == null ? (object)instruction : new InstructionReference(method, instruction); writer.WriteReference(DnlibExtensions.OffsetToString(instruction.GetOffset()), r, tokenType); }
public static void WriteTo(this ITypeDefOrRef type, ITextOutput writer, ILNameSyntax syntax, int depth) { if (depth++ > MAX_CONVERTTYPE_DEPTH || type == null) { return; } var ts = type as TypeSpec; if (ts != null && !(ts.TypeSig is FnPtrSig)) { WriteTo(((TypeSpec)type).TypeSig, writer, syntax, depth); return; } string typeFullName = type.FullName; string typeName = type.Name.String; if (ts != null) { var fnPtrSig = ts.TypeSig as FnPtrSig; typeFullName = DnlibExtensions.GetFnPtrFullName(fnPtrSig); typeName = DnlibExtensions.GetFnPtrName(fnPtrSig); } TypeSig typeSig = null; string name = type.DefinitionAssembly.IsCorLib() ? PrimitiveTypeName(typeFullName, type.Module, out typeSig) : null; if (syntax == ILNameSyntax.ShortTypeName) { if (name != null) { WriteKeyword(writer, name, typeSig.ToTypeDefOrRef()); } else { writer.WriteReference(Escape(typeName), type, TextTokenHelper.GetTextTokenType(type)); } } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) { WriteKeyword(writer, name, typeSig.ToTypeDefOrRef()); } else { if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) { writer.Write(DnlibExtensions.IsValueType(type) ? "valuetype" : "class", TextTokenType.Keyword); writer.WriteSpace(); } if (type.DeclaringType != null) { type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName, depth); writer.Write('/', TextTokenType.Operator); writer.WriteReference(Escape(typeName), type, TextTokenHelper.GetTextTokenType(type)); } else { if (!(type is TypeDef) && type.Scope != null && !(type is TypeSpec)) { writer.Write('[', TextTokenType.Operator); writer.Write(Escape(type.Scope.GetScopeName()), TextTokenType.ILModule); writer.Write(']', TextTokenType.Operator); } if (ts != null || MustEscape(typeFullName)) { writer.WriteReference(Escape(typeFullName), type, TextTokenHelper.GetTextTokenType(type)); } else { WriteNamespace(writer, type.Namespace); if (!string.IsNullOrEmpty(type.Namespace)) { writer.Write('.', TextTokenType.Operator); } writer.WriteReference(IdentifierEscaper.Escape(type.Name), type, TextTokenHelper.GetTextTokenType(type)); } } } }
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction, TextTokenType tokenType = TextTokenType.Label) { writer.WriteReference(DnlibExtensions.OffsetToString(instruction.GetOffset()), instruction, tokenType); }
public void WriteTo(TextWriter writer) { foreach (Instruction prefix in this.Prefixes) { Disassembler.DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer)); writer.WriteLine(); } if (Instruction != null && Instruction.Offset >= 0) { writer.Write(DnlibExtensions.OffsetToString(Instruction.Offset)); writer.Write(": "); } if (Target != null) { writer.Write(Target.ToString()); writer.Write(" = "); } if (IsMoveInstruction) { writer.Write(Operands[0].ToString()); if (Instruction != null) { writer.Write(" (" + Instruction.OpCode.Name + ")"); } } else { if (Instruction == null) { writer.Write(SpecialOpCode.ToString()); } else { writer.Write(Instruction.OpCode.Name); if (null != Instruction.Operand) { writer.Write(' '); Disassembler.DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand); writer.Write(' '); } } if (TypeOperand != null) { writer.Write(' '); writer.Write(TypeOperand.ToString()); writer.Write(' '); } if (Operands.Length > 0) { writer.Write('('); for (int i = 0; i < Operands.Length; i++) { if (i > 0) { writer.Write(", "); } writer.Write(Operands[i].ToString()); } writer.Write(')'); } } }
object ResolveGenericParams(object operand) { return(DnlibExtensions.ResolveGenericParams(typeParams, methodParams, operand)); }
static MethodDef GetMethodDefinition(IMethod method) { return(DnlibExtensions.ResolveMethodWithinSameModule(method)); }
static FieldDef GetFieldDefinition(IField field) { return(DnlibExtensions.ResolveFieldWithinSameModule(field)); }
bool MatchTaskCreationPattern(ILBlock method) { if (method.Body.Count < 5) return false; // Check the second-to-last instruction (the start call) first, as we can get the most information from that IMethod startMethod; ILExpression loadStartTarget, loadStartArgument; // call(AsyncTaskMethodBuilder::Start, ldloca(builder), ldloca(stateMachine)) if (!method.Body[method.Body.Count - 2].Match(ILCode.Call, out startMethod, out loadStartTarget, out loadStartArgument)) return false; if (startMethod.Name != "Start" || startMethod.DeclaringType == null || startMethod.DeclaringType.Namespace != "System.Runtime.CompilerServices") return false; switch (startMethod.DeclaringType.Name) { case "AsyncTaskMethodBuilder`1": methodType = AsyncMethodType.TaskOfT; break; case "AsyncTaskMethodBuilder": methodType = AsyncMethodType.Task; break; case "AsyncVoidMethodBuilder": methodType = AsyncMethodType.Void; break; default: return false; } ILVariable stateMachineVar, builderVar; if (!loadStartTarget.Match(ILCode.Ldloca, out builderVar)) return false; if (!loadStartArgument.Match(ILCode.Ldloca, out stateMachineVar)) return false; stateMachineStruct = stateMachineVar.Type.GetTypeDefOrRef().ResolveWithinSameModule(); if (stateMachineStruct == null || !DnlibExtensions.IsValueType(stateMachineStruct)) return false; moveNextMethod = stateMachineStruct.Methods.FirstOrDefault(f => f.Name == "MoveNext"); if (moveNextMethod == null) return false; // Check third-to-last instruction (copy of builder): // stloc(builder, ldfld(StateMachine::<>t__builder, ldloca(stateMachine))) ILExpression loadBuilderExpr; if (!method.Body[method.Body.Count - 3].MatchStloc(builderVar, out loadBuilderExpr)) return false; IField builderFieldRef; ILExpression loadStateMachineForBuilderExpr; if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr)) return false; if (!(loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdloc(stateMachineVar))) return false; builderField = builderFieldRef.ResolveFieldWithinSameModule(); if (builderField == null) return false; // Check the last instruction (ret) if (methodType == AsyncMethodType.Void) { if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret)) return false; } else { // ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine)))) ILExpression returnValue; if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret, out returnValue)) return false; IMethod getTaskMethod; ILExpression builderExpr; if (!returnValue.Match(ILCode.Call, out getTaskMethod, out builderExpr)) return false; ILExpression loadStateMachineForBuilderExpr2; IField builderField2; if (!builderExpr.Match(ILCode.Ldflda, out builderField2, out loadStateMachineForBuilderExpr2)) return false; if (builderField2.ResolveFieldWithinSameModule() != builderField || !loadStateMachineForBuilderExpr2.MatchLdloca(stateMachineVar)) return false; } // Check the last field assignment - this should be the state field ILExpression initialStateExpr; if (!MatchStFld(method.Body[method.Body.Count - 4], stateMachineVar, out stateField, out initialStateExpr)) return false; if (!initialStateExpr.Match(ILCode.Ldc_I4, out initialState)) return false; if (initialState != -1) return false; // Check the second-to-last field assignment - this should be the builder field FieldDef builderField3; ILExpression builderInitialization; if (!MatchStFld(method.Body[method.Body.Count - 5], stateMachineVar, out builderField3, out builderInitialization)) return false; IMethod createMethodRef; if (builderField3 != builderField || !builderInitialization.Match(ILCode.Call, out createMethodRef)) return false; if (createMethodRef.Name != "Create") return false; for (int i = 0; i < method.Body.Count - 5; i++) { FieldDef field; ILExpression fieldInit; if (!MatchStFld(method.Body[i], stateMachineVar, out field, out fieldInit)) return false; ILVariable v; if (!fieldInit.Match(ILCode.Ldloc, out v)) return false; if (!v.IsParameter) return false; fieldToParameterMap[field] = v; } return true; }
/// <summary> /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="parent">The direct parent of the load within 'next'</param> /// <param name="pos">Index of the load within 'parent'</param> /// <param name="v">The variable being inlined.</param> /// <param name="inlinedExpression">The expression being inlined</param> bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression) { if (pos == 0 && v.Type != null && DnlibExtensions.IsValueType(v.Type)) { // Inlining a value type variable is allowed only if the resulting code will maintain the semantics // that the method is operating on a copy. // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers switch (inlinedExpression.Code) { case ILCode.Ldloc: case ILCode.Stloc: case ILCode.CompoundAssignment: case ILCode.Ldelem: case ILCode.Ldelem_I: case ILCode.Ldelem_I1: case ILCode.Ldelem_I2: case ILCode.Ldelem_I4: case ILCode.Ldelem_I8: case ILCode.Ldelem_R4: case ILCode.Ldelem_R8: case ILCode.Ldelem_Ref: case ILCode.Ldelem_U1: case ILCode.Ldelem_U2: case ILCode.Ldelem_U4: case ILCode.Ldobj: case ILCode.Ldind_Ref: return(false); case ILCode.Ldfld: case ILCode.Stfld: case ILCode.Ldsfld: case ILCode.Stsfld: // allow inlining field access only if it's a readonly field FieldDef f = ((IField)inlinedExpression.Operand).Resolve(); if (!(f != null && f.IsInitOnly)) { return(false); } break; case ILCode.Call: case ILCode.CallGetter: // inlining runs both before and after IntroducePropertyAccessInstructions, // so we have to handle both 'call' and 'callgetter' IMethod mr = (IMethod)inlinedExpression.Operand; // ensure that it's not an multi-dimensional array getter TypeSig ts; if (mr.DeclaringType is TypeSpec && (ts = ((TypeSpec)mr.DeclaringType).TypeSig.RemovePinnedAndModifiers()) != null && ts.IsSingleOrMultiDimensionalArray) { return(false); } goto case ILCode.Callvirt; case ILCode.Callvirt: case ILCode.CallvirtGetter: // don't inline foreach loop variables: mr = (IMethod)inlinedExpression.Operand; if (mr.Name == "get_Current" && mr.MethodSig != null && mr.MethodSig.HasThis) { return(false); } break; case ILCode.Castclass: case ILCode.Unbox_Any: // These are valid, but might occur as part of a foreach loop variable. ILExpression arg = inlinedExpression.Arguments[0]; if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) { mr = (IMethod)arg.Operand; if (mr.Name == "get_Current" && mr.MethodSig != null && mr.MethodSig.HasThis) { return(false); // looks like a foreach loop variable, so don't inline it } } break; } // inline the compiler-generated variable that are used when accessing a member on a value type: switch (parent.Code) { case ILCode.Call: case ILCode.CallGetter: case ILCode.CallSetter: case ILCode.Callvirt: case ILCode.CallvirtGetter: case ILCode.CallvirtSetter: IMethod mr = parent.Operand as IMethod; return(mr == null || mr.MethodSig == null ? false : mr.MethodSig.HasThis); case ILCode.Stfld: case ILCode.Ldfld: case ILCode.Ldflda: case ILCode.Await: return(true); } } return(false); }