public static AnalyzerData analyze( IEnumerable <MethodDefinition> entryPoints, AnalyserLogger log ) { var data = new AnalyzerData( ImmutableHashSet <ExpandedType> .Empty, ImmutableHashSet <ExpandedMethod> .Empty, ImmutableHashSet <VirtualMethod> .Empty, ImmutableHashSet <VirtualMethod> .Empty ); foreach (var entryMethod in entryPoints) { var exEntryMethod = ExpandedMethod.create( entryMethod, ExpandedType.EMPTY_GENERIC_LOOKUP ); data = data.addType(exEntryMethod.declaringType); if (!data.hasMethod(exEntryMethod)) { log.log("Entry", exEntryMethod); data = analyze(data, exEntryMethod, log); } } // Once we know all used types, resolve the called virtual methods. // Iterate in a loop because expanding virtual method bodies might invoke additional // virtual methods. while (!data.virtualMethodsToAnalyze.IsEmpty) { foreach (var virtualMethod in data.virtualMethodsToAnalyze) { var declaring = virtualMethod.method.declaringType; foreach (var _usedType in data.usedTypes.Where(et => et.implements(declaring))) { var usedType = _usedType; MethodDefinition matching = null; while (matching == null) { matching = VirtualMethods.GetMethod( usedType.definition.Methods, virtualMethod.method.definition ); if (matching == null) { if (usedType.definition.BaseType == null) { throw new Exception( "Can't find implementation for [" + virtualMethod + "] in [" + _usedType + "]!" ); } usedType = ExpandedType.create( usedType.definition.BaseType, usedType.genericParametersToArguments ); } } var generics = usedType.genericParametersToArguments.AddRange( ExpandedMethod.genericArgumentsDict( virtualMethod.method.genericArguments.Select(t => (TypeReference)t.definition).ToList(), matching.GenericParameters, usedType.genericParametersToArguments ) ); var exMatching = new ExpandedMethod( virtualMethod.method.returnType, usedType, matching, virtualMethod.method.genericArguments, virtualMethod.method.parameters, generics ); data = analyze(data, exMatching, log); } data = data.analyzedVirtualMethod(virtualMethod); } } return(data); }
static AnalyzerData analyze( AnalyzerData data, ExpandedMethod method, AnalyserLogger log ) { if (data.hasMethod(method)) { return(data); } log.log("method", method); log.incIndent(); data = data.addMethod(method); if (method.definition.IsConstructor) { data = data.addType(method.declaringType); } data = data.addType(method.returnType); data = method.parameters.Aggregate(data, (current, parameter) => current.addType(parameter)); if (method.definition.IsVirtual && method.definition.Body == null) { // Because we don't know with what concrete types the implementation classes // of virtual methods will be instantiated, we will need to do a separate // analyzing stage once we know all the concrete types. data = data.addType(method.declaringType); data = data.addVirtualMethod(new VirtualMethod(method)); } else if (method.definition.Body != null) { var body = method.definition.Body; foreach (var varDef in body.Variables) { var exVar = ExpandedType.create(varDef.VariableType, method.genericParameterToExType); data = data.addType(exVar); } foreach (var instruction in body.Instructions) { if ( instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld || instruction.OpCode == OpCodes.Ldsfld ) { var fieldDef = (FieldReference)instruction.Operand; var fieldGenerics = method.genericParameterToExType; var gFieldType = fieldDef.FieldType as GenericInstanceType; if (fieldDef.FieldType.DeclaringType != null /*fieldDef.FieldType.IsNested*/) { if (fieldDef.FieldType.IsGenericParameter) { var generics = ExpandedMethod.genericArgumentsDict( ((GenericInstanceType)fieldDef.DeclaringType).GenericArguments, fieldDef.FieldType.DeclaringType.GenericParameters, fieldGenerics ); fieldGenerics = fieldGenerics.AddRange(generics); } if (gFieldType != null) { var tpl = gFieldType.GenericArguments.SequenceEqual(gFieldType.ElementType.GenericParameters) ? F.t( (IList <TypeReference>)gFieldType.ElementType.DeclaringType.GenericParameters.Cast <TypeReference>().ToList(), (IList <GenericParameter>)gFieldType.GenericArguments.Cast <GenericParameter>().ToList() ) : gFieldType.ElementType.DeclaringType.HasGenericParameters ? F.t( (IList <TypeReference>)gFieldType.GenericArguments, (IList <GenericParameter>)gFieldType.ElementType.DeclaringType.GenericParameters ) : F.t( (IList <TypeReference>) new TypeReference[0], (IList <GenericParameter>) new GenericParameter[0] ); var generics = ExpandedMethod.genericArgumentsDict(tpl._1, tpl._2, fieldGenerics); fieldGenerics = fieldGenerics.AddRange(generics); } } var exFieldDef = ExpandedType.create(fieldDef.FieldType, fieldGenerics); var exDeclaringType = ExpandedType.create(fieldDef.DeclaringType, method.genericParameterToExType); data = data.addType(exFieldDef).addType(exDeclaringType); } else if ( instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Calli || instruction.OpCode == OpCodes.Callvirt || instruction.OpCode == OpCodes.Newobj ) { var methodRef = (MethodReference)instruction.Operand; if (instruction.OpCode == OpCodes.Newobj && methodRef.DeclaringType.Resolve().isDelegate()) { methodRef = (MethodReference)instruction.Previous.Operand; } var expanded = ExpandedMethod.create(methodRef, method.genericParameterToExType); data = analyze(data, expanded, log); } } } else if (!(method.definition.IsInternalCall || method.definition.IsPInvokeImpl)) { throw new Exception("Method body null when not expected: " + method); } log.decIndent(); return(data); }