/// <summary> /// Resolves the outbound call stack for the given <see cref="token"/> /// </summary> /// <param name="token"></param> /// <param name="depth"></param> /// <param name="stackTrc">For detecting recursive call patterns</param> /// <param name="msgOut">For getting details on recursion.</param> internal void ResolveCallOfCall(MetadataTokenId token, ref int depth, Stack <MetadataTokenId> stackTrc, StringBuilder msgOut) { if (msgOut != null) { if (depth > 0) { msgOut.Append(new string(' ', depth)); } msgOut.AppendFormat("Depth:{0}", depth); msgOut.AppendFormat(", Token:{0}.0x{1}", token.RslvAsmIdx, token.Id.ToString("X4")); } //detect if we are in a recursive call if (stackTrc.Any(x => x.Equals(token))) { if (msgOut != null) { msgOut.AppendLine(", Message:'present in stack trace'"); } return; } stackTrc.Push(new MetadataTokenId { Id = token.Id, RslvAsmIdx = token.RslvAsmIdx }); //increment the current depth depth += 1; //abort if max depth has been reached if (depth > ((IaaProgram)MyProgram).MaxRecursionDepth) { depth -= 1; MyProgram.PrintToConsole( String.Format("Max Recursion Depth @ {0}.{1}\n", token.RslvAsmIdx, token.Id)); if (msgOut != null) { msgOut.AppendLine(", Message:'max recursion depth'"); } return; } //when there are already Items then leave them as is if (token.Items != null && token.Items.Length > 0) { depth -= 1; if (msgOut != null) { msgOut.AppendLine(", Message:'Items already present'"); } return; } //don't waste clock cycles on Ignore types if (((IaaProgram)MyProgram).DisolutionCache.Contains(token)) { depth -= 1; if (msgOut != null) { msgOut.AppendLine(", Message:'token in DisolutionCache'"); } return; } //resolve token to name MetadataTokenName tokenName = null; if (((IaaProgram)MyProgram).TokenId2NameCache.ContainsKey(token)) { tokenName = ((IaaProgram)MyProgram).TokenId2NameCache[token]; } else { // the token must be resolvable with the its manifest module var resolveRslt = ((IaaProgram)MyProgram).UtilityMethods.ResolveSingleTokenName(token, out tokenName, msgOut); if (!resolveRslt || tokenName == null) { ((IaaProgram)MyProgram).DisolutionCache.Add(token); depth -= 1; if (msgOut != null) { msgOut.AppendLine(", Message:'ResolveSingleTokenName failed'"); } return; } ((IaaProgram)MyProgram).TokenId2NameCache.Add(token, tokenName); } //only proceed to find calls of calls, types are resolved elsewhere if (!tokenName.IsMethodName()) { depth -= 1; if (msgOut != null) { msgOut.AppendLine(", Message:'token is not a method'"); } return; } //match is on Asm Name, not type nor member name var owningAsmName = ((IaaProgram)MyProgram).AsmIndicies.Asms.FirstOrDefault(x => x.IndexId == tokenName.OwnAsmIdx); if (owningAsmName == null) { ((IaaProgram)MyProgram).DisolutionCache.Add(token); depth -= 1; if (msgOut != null) { msgOut.AppendLine(string.Format(", Message:'owning assembly idx {0} has no match'", tokenName.OwnAsmIdx)); } return; } //check for match of asm name to user defined regex if (!Regex.IsMatch(owningAsmName.AssemblyName, ((IaaProgram)MyProgram).AssemblyNameRegexPattern)) { ((IaaProgram)MyProgram).DisolutionCache.Add(token); if (msgOut != null) { msgOut.AppendLine(string.Format(", Message:'assembly name [{1}] does not match regex [{0}]'", ((IaaProgram)MyProgram).AssemblyNameRegexPattern, owningAsmName.AssemblyName)); } depth -= 1; return; } //resolve token to runtime member info MemberInfo mi; var rtMiRslt = ((IaaProgram)MyProgram).UtilityMethods.TryResolveRtMemberInfo(((IaaProgram)MyProgram).AsmIndicies.GetAssemblyByIndex(owningAsmName.IndexId), tokenName.Name, out mi, msgOut); if (!rtMiRslt) { ((IaaProgram)MyProgram).DisolutionCache.Add(token); depth -= 1; return; } //get the token as it is called in its own assembly var r = AssemblyAnalysis.GetMetadataToken(mi, true, tokenName.OwnAsmIdx); if (stackTrc.Any(x => x.Equals(r))) { depth -= 1; token.IsByRef = 1; if (msgOut != null) { msgOut.AppendLine(string.Format(", Message:'name resolved token id {0},{1} found in stack trace'", r.RslvAsmIdx, r.Id.ToString("X4"))); } return; } //this token differs from the method arg 'token' stackTrc.Push(new MetadataTokenId { Id = r.Id, RslvAsmIdx = r.RslvAsmIdx }); //unwind when its a terminal token if (r.Items == null || r.Items.Length <= 0) { depth -= 1; if (msgOut != null) { msgOut.AppendLine(string.Format(", Message:'name resolved token id {0},{1} is a terminal node'", r.RslvAsmIdx, r.Id.ToString("X4"))); } return; } //these token ids are only resolvable to the asm who owns MemberInfo(mi) token.Items = r.Items; //recurse each of these calls-of-calls[...]-of-calls foreach (var iToken in token.Items) { ResolveCallOfCall(iToken, ref depth, stackTrc, msgOut); } depth -= 1; }
/// <summary> /// Locates the <see cref="CgMember"/> in <see cref="Methods"/> who matches <see cref="tokenName"/>. /// </summary> /// <param name="tokenName"></param> /// <returns></returns> public CgMember FindCgMethodByTokenName(MetadataTokenName tokenName) { if (tokenName == null) return null; if (string.IsNullOrWhiteSpace(tokenName.Name)) return null; if (!tokenName.IsMethodName()) return null; var methodName = AssemblyAnalysis.ParseMethodNameFromTokenName(tokenName.Name); if (string.IsNullOrWhiteSpace(methodName)) return null; string isPropName; if (NfTypeName.IsClrMethodForProperty(tokenName.Name, out isPropName)) { methodName = isPropName; var propMatches = Properties.Where(p => string.Equals(p.Name, methodName)).ToArray(); if (propMatches.Length >= 1) return propMatches.First(); } var matches = Methods.Where(x => string.Equals(x.Name, methodName)).ToArray(); if (matches.Length <= 0) return null; if (matches.Length == 1) return matches.First(); //attempt to match on arg count first var argNames = AssemblyAnalysis.ParseArgsFromTokenName(tokenName.Name); var argCount = argNames == null ? 0 : argNames.Length; matches = Methods.Where(x => x.Args.Count == argCount).ToArray(); if (matches.Length <= 0) return null; if (matches.Length == 1) return matches.First(); if (argNames == null) return null; //attempt to match by args names var argNamesLikeThese = argNames.Select(x => Settings.LangStyle.TransformClrTypeSyntax(x)); foreach (var match in matches) { if (match.Args.All(nfArg => argNamesLikeThese.Any(token => string.Equals(nfArg.ArgType, token)))) return match; } return null; }