/// <summary> /// Creates a new wrapper around the remote process. /// </summary> /// <param name="ports"></param> /// <remarks> /// See the detailed notes on its sister type <see cref="AssemblyAnalysis"/> ctor. /// While not exact in terms of what its doing - it is exact in terms of how it does it. /// </remarks> public Flatten(params int[] ports) { if (string.IsNullOrWhiteSpace(NfConfig.CustomTools.InvokeFlatten) || !File.Exists(NfConfig.CustomTools.InvokeFlatten)) { throw new ItsDeadJim("Don't know where to locate the NoFuture.Util.Tokens.InvokeFlatten.exe, assign " + "the global variable at NoFuture.Tools.CustomTools.InvokeFlatten."); } var args = string.Empty; var getFlatAsmPort = DefaultPort; if (ports != null && ports.Length > 0) { getFlatAsmPort = ports[0]; args = ConsoleCmd.ConstructCmdLineArgs(GET_FLAT_ASM_PORT_CMD_SWITCH, getFlatAsmPort.ToString(CultureInfo.InvariantCulture)); } MyProcess = StartRemoteProcess(NfConfig.CustomTools.InvokeFlatten, args); _invokeGetFlattenAssemblyCmd = new InvokeGetFlattenAssembly() { ProcessId = MyProcess.Id, SocketPort = getFlatAsmPort }; }
/// <summary> /// The ctor both instantiates this type and launches the remote process which does /// the actual parsing. /// </summary> /// <param name="port"></param> public NfTypeNameProcess(int?port) { //is there one already running? MyProcess = System.Diagnostics.Process.GetProcessesByName("NoFuture.Tokens.InvokeNfTypeName").FirstOrDefault(); if (MyProcess != null) { _invokeCmd = new InvokeGetNfTypeName { ProcessId = MyProcess.Id, SocketPort = port.GetValueOrDefault(DefaultPort) }; return; } if (string.IsNullOrWhiteSpace(NfConfig.CustomTools.InvokeNfTypeName) || !File.Exists(NfConfig.CustomTools.InvokeNfTypeName)) { throw new ItsDeadJim("Don't know where to locate the NoFuture.Tokens.InvokeNfTypeName.exe, assign " + "the global variable at NoFuture.Tools.CustomTools.InvokeNfTypeName."); } var cmdPort = port.GetValueOrDefault(DefaultPort); var args = ConsoleCmd.ConstructCmdLineArgs(GET_NF_TYPE_NAME_CMD_SWITCH, cmdPort.ToString()); MyProcess = StartRemoteProcess(NfConfig.CustomTools.InvokeNfTypeName, args); _invokeCmd = new InvokeGetNfTypeName { ProcessId = MyProcess.Id, SocketPort = cmdPort }; }
/// <summary> /// Runs <see cref="AsmDiagram"/> to generate a diagram /// on another process to keep the loaded assemblies isolated from the invoking app domain. /// </summary> /// <param name="assemblyPath"></param> /// <returns>This output is JSON and not GraphViz syntax</returns> public static string RunIsolatedAsmAdjGraph(string assemblyPath) { var argPath = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_ASM_PATH_SWITCH, assemblyPath); var diagramType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DIAGRAM_TYPE, Settings.ASM_ADJ_GRAPH); return(RunGraphViz(argPath, null, diagramType, 60)); }
/// <summary> /// Run its counterpart <see cref="GetClassDiagram"/> in an isolated process /// </summary> public static string RunIsolatedGetClassDiagram(string assemblyPath, string typeFullName, bool displayEnums, int maxWaitInSeconds = 60) { var argPath = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_ASM_PATH_SWITCH, assemblyPath); var argType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_FULL_TYPE_NAME_SWITCH, typeFullName); var diagramType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DIAGRAM_TYPE, Settings.CLASS_DIAGRAM); var argEn = displayEnums ? ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DISPLAY_ENUMS, null) : null; return(RunGraphViz(argPath, argType, diagramType, maxWaitInSeconds, argEn)); }
/// <summary> /// Runs <see cref="AsmDiagram"/> to generate a diagram /// on another process to keep the loaded assemblies isolated from the invoking app domain. /// </summary> /// <param name="assemblyPath"></param> /// <param name="withNamespaceSubgraphs"></param> /// <returns></returns> public static string RunIsolatedAsmDiagram(string assemblyPath, bool withNamespaceSubgraphs = false) { var argPath = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_ASM_PATH_SWITCH, assemblyPath); var diagramType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DIAGRAM_TYPE, Settings.ASM_OBJ_GRAPH_DIAGRAM); if (withNamespaceSubgraphs) { var withNsOutlines = ConsoleCmd.ConstructCmdLineArgs(Settings.ASM_OBJ_OUTLINE_NS, null); return(RunGraphViz(argPath, null, diagramType, 60, withNsOutlines)); } return(RunGraphViz(argPath, null, diagramType, 60)); }
/// <summary> /// A helper function used to draft the entire command line switch (s) /// which are passed to the NoFuture.Hbm.InvokeStoredProc.exe process. /// /// The function handles wrapping values in double quotes when said value /// contains a space. When the args are assigned to a <see cref="System.Diagnostics.ProcessStartInfo"/> /// as a single string and, in turn, received by a console's Main statement as a /// string array the values are being split on the space char (0x20) and the double-quotes /// are removed. /// </summary> /// <param name="spItem"></param> /// <param name="connectionString"></param> /// <returns></returns> public static string ConstructCmdLineArgs(StoredProcMetadata spItem, string connectionString) { var cmdArg = new StringBuilder(); cmdArg.Append(ConsoleCmd.ConstructCmdLineArgs(CONNECTION_STR_SWITCH, connectionString)); cmdArg.Append(" "); cmdArg.Append(ConsoleCmd.ConstructCmdLineArgs(FILE_PATH_SWITCH, spItem.BinFilePath)); cmdArg.Append(" "); cmdArg.Append(ConsoleCmd.ConstructCmdLineArgs(HBM_STORED_PROX_DIR_SWITCH, Settings.HbmStoredProcsDirectory)); cmdArg.Append(" "); cmdArg.Append(ConsoleCmd.ConstructCmdLineArgs(SEND_MESSAGES_BACK_ON_SOCKET, Boolean.TrueString)); return(cmdArg.ToString()); }
/// <summary> /// Runs <see cref="Flatten"/> /// </summary> /// <param name="assemblyPath"></param> /// <param name="typeFullName"></param> /// <param name="maxDepth">The max recursive depth.</param> /// <param name="onlyPrimitivesNamed"></param> /// <param name="displayEnums"></param> /// <param name="maxWaitInSeconds"></param> /// <returns></returns> public static string RunIsolatedFlattenTypeDiagram(string assemblyPath, string typeFullName, string onlyPrimitivesNamed, bool displayEnums, int maxDepth = FlattenLineArgs.MAX_DEPTH, int maxWaitInSeconds = 60) { var argPath = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_ASM_PATH_SWITCH, assemblyPath); var argType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_FULL_TYPE_NAME_SWITCH, typeFullName); var diagramType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DIAGRAM_TYPE, Settings.FLATTENED_DIAGRAM); var argP = !string.IsNullOrWhiteSpace(onlyPrimitivesNamed) ? ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_FLATTENED_LIMIT_TYPE, onlyPrimitivesNamed) : null; var argEn = displayEnums ? ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_DISPLAY_ENUMS, null) : null; var argMaxDepth = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_GRAPHVIZ_FLATTEN_MAX_DEPTH, maxDepth.ToString()); return(RunGraphViz(argPath, argType, diagramType, maxWaitInSeconds, argP, argEn, argMaxDepth)); }
public Dpx(string binDir) { if (string.IsNullOrWhiteSpace(binDir) || !Directory.Exists(binDir)) { throw new ItsDeadJim($"The {binDir} directory was not found"); } if (string.IsNullOrWhiteSpace(NfConfig.CustomTools.InvokeDpx) || !File.Exists(NfConfig.CustomTools.InvokeDpx)) { throw new ItsDeadJim("Don't know where to locate the NoFuture.Tokens.DotNetMeta.InvokeDpx, assign " + "the global variable at NoFuture.CustomTools.InvokeDpx."); } var args = string.Join(" ", ConsoleCmd.ConstructCmdLineArgs(BIN_DIR, binDir)); _si = new ProcessStartInfo { FileName = NfConfig.CustomTools.InvokeDpx, Arguments = args, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; }
/// <summary> /// Launches the NoFuture.Tokens.Gia.InvokeAssemblyAnalysis.exe /// </summary> /// <param name="resolveGacAsmNames"></param> /// <param name="ports"> /// Optional, allows caller to specify the ports used - will use defaults /// otherwise. /// </param> /// <returns></returns> /// <remarks> /// The ports used are <see cref="DefaultPort"/> to <see cref="DefaultPort"/> + 5. /// </remarks> /// <remarks> /// <![CDATA[ /// +------------------------------------------------------------------------------------------------------------+ /// | Using AssemblyAnalysis | /// ||+--operating-context(1)--+|+-----new-instance(2)----+|+------remote-exe(3)-----+|+---target-assembly(4)---+| /// || new instance of | | | | /// || ................> | | | /// || | start new process | | | /// || | provide assembly name | | | /// || | ................> | | /// || | | launch sockets | | /// || | | get asm names on manifest| | /// || | | ................> | /// || | |send AsmIndicies on socket| | /// || | <................ | | /// || | receive AsmIndices | | | /// || | save to disk | | | /// || | assign to prop | | | /// || <................ | | | /// || invoke GetTokenIds with | | | | /// || regex | | | | /// || ................> | | | /// || | send GetTokenIdsCriteria | | | /// || | on socket | | | /// || | ................> | | /// || | | get types as tokens | | /// || | | ................> | /// || | | get members as tokens | | /// || | | ................> | /// || | | get callvirts as tokens | | /// || | | ................> | /// || | | get tokens-of-tokens(a) | | /// || | | ................> | /// || | |send TokenIds on socket(b)| | /// || | <................ | | /// || | get TokenIds | | | /// || | save to disk | | | /// || | return TokenIds | | | /// || <................ | | | /// || flatten the TokenIds | | | | /// || invoke GetTokenNames | | | | /// || ................> | | | /// || | send MetadataTokenId[] on| | | /// || | socket | | | /// || | ................> | | /// || | | resolve each to a runtime| | /// || | | type | | /// || | | ................> | /// || | | send TokenNames on socket| | /// || | <................ | | /// || | receive TokenNames | | | /// || | save to disk | | | /// || | return TokenNames | | | /// || <................ | | | /// ||+------------------------+|+------------------------+|+------------------------+|+------------------------+| /// /// (1) assume PowerShell /// (2) new instance of NoFuture.Tokens.DotNetMeta.AssemblyAnalysis /// (3) new process NoFuture.Tokens.DotNetMeta.InvokeAssemblyAnalysis.exe /// (4) the assembly whose tokens we want /// /// (a) The top-level types already had all thier members resolved to tokens and every virtcall found within the body of those members. /// This is now starting it all over again as if the types found on the callvirts in the body of these members were themselves the top-level types. /// This will continue until we end up on a type which is contained in an assembly whose name doesn't match our regex pattern. /// (b) This is a tree data-struct. Each token has itself a collection of tokens and so on. /// ]]> /// </remarks> /// <example> /// <![CDATA[ /// # will launch a console /// $myAsmAly = New-Object NoFuture.Tokens.DotNetMeta.AssemblyAnalysis($false) /// /// # this is assembly-name-to-index used to reduce the size of socket payloads /// $myAsmIndices = $myAsmAly.GetAsmIndices($AssemblyPath) /// /// # this is all the types in a similar form /// $myTokenTypes = $myAsmAly.GetTokenTypes("NoFuture.*") /// /// # this will represent the call-stack-tree in terms of just metadata token ids /// $myTokensIds = $myAsmAly.GetTokenIds(0, "NoFuture.*") /// /// # translates the metadata token ids into a token name (e.g. type, method, field, etc.) /// $myTokenNames = $myAsmAly.GetTokenNames($myTokensIds.GetAsRoot().SelectDistinct(), $true) /// ]]> /// </example> /// <example> /// <![CDATA[ /// # example using analysis results /// # say, some app with three layers: web, logic and data /// # have already run the analysis and have all the results on file as JSON /// $myAsmAly = New-Object NoFuture.Tokens.DotNetMeta.AssemblyAnalysis($false) /// /// # web layer uses types from logic layer as interfaces /// $myWebTokens = ([NoFuture.Tokens.DotNetMeta.TokenName.TokenNameResponse]::ReadFromFile($webTokensFile)).GetAsRoot() /// $myWebTypes = ([NoFuture.Tokens.DotNetMeta.TokenType.TokenTypeResponse]::ReadFromFile($webTypesFile)).GetAsRoot() /// $myWebAssemblies = [NoFuture.Tokens.DotNetMeta.TokenAsm.TAsmIndexResponse]::ReadFromFile($webAssemblyFile) /// /// #logic layer uses types from data layer, likewise, as interfaces /// $myLogicTokens = ([NoFuture.Tokens.DotNetMeta.TokenName.TokenNameResponse]::ReadFromFile($logicTokensFile)).GetAsRoot() /// $myLogicTypes = ([NoFuture.Tokens.DotNetMeta.TokenType.TokenTypeResponse]::ReadFromFile($logicTypesFile)).GetAsRoot() /// /// #data layer is as far down as we want to go /// $myDataTokens = ([NoFuture.Tokens.DotNetMeta.TokenName.TokenNameResponse]::ReadFromFile($dataTokensFile)).GetAsRoot() /// $myDataTypes = ([NoFuture.Tokens.DotNetMeta.TokenType.TokenTypeResponse]::ReadFromFile($dataTypesFile)).GetAsRoot() /// /// #expand logic layer with data layer's concrete types /// $myLogicTokens = $myAsmAly.ReassignTokenNames($myLogicTokens, $myDataTokens, $myDataTypes).GetAsRoot() /// /// #expand web layer with expanded logic layer's concrete types /// $myWebTokens = $myAsmAly.ReassignTokenNames($myWebTokens, $myLogicTokens, $myLogicTypes).GetAsRoot() /// /// #get all token names in logic layer as a Set instead of a Data-Tree /// $myFlatLogicTokens = $myLogicTokens.SelectDistinct() /// /// #do likewise for the web layer - get tokens as a Set /// $myFlatWebTokens = $myWebTokens.SelectDistinct() /// /// #now normal Set-Operations can be applied /// #say, want to find orphaned methods in logic layer no longer used by web layer /// $myOrphanedLogicTokens = $myFlatWebTokens.GetRightSetDiff($myFlatLogicTokens) /// /// #this would tell us all the types with at least one orphaned member /// $orphanedTypes = $myOrphanedLogicTokens.GetUniqueTypeNames() | Sort-Object /// /// #using NoFuture.Gen, we could remove these systematically if we know where to find the assemblies with .pdb's /// $searchDirs = @( /// "C:\Projects\MyProj\Web\bin", /// "C:\Projects\MyProj\Logic\debug\bin", /// "C:\Projects\MyProj\Data\debug\bin") /// /// /// #(in actual practice, you would want to perform null-checks, skipped here for brevity) /// $orphanedTypes | % { /// $typeName = $_ /// /// $tokenType = $myWebTypes.Items | ? {$_.Name -eq $typeName} /// /// #PDB data will not be found for interface type def's /// if($tokenType.IsInterfaceType()){ /// $tokenType = $tokenTypes.GetFirstInterfaceImplementor($tokenType) /// } /// /// #need to go backwards from a type name to some path to some assembly on the drive /// $asmPath = [NoFuture.Tokens.DotNetMeta.TokenAsm.AsmIndexResponse]::GetAssemblyPathFromRoot($typeName, $myWebAssemblies, $tokenType, $searchDirs) /// /// #now get this as a NoFuture.Gen code-gen type (assuming .NET code file was C#) /// $nfCgType = New-Object NoFuture.Gen.CgTypeCsSrcCode($asmPath,$typeName) /// /// #collect up all the orphaned members now as NoFuture.Gen.CgMember's /// $nfCgMems = New-Object "System.Collections.Generic.List[NoFuture.Gen.CgMember]" /// $orphanedMembersByType = $myOrphanedLogicTokens.SelectTypeNamesThatEndWith(([string[]]@($typeName))) /// $orphanedMembersByType | % { /// /// #NoFuture.Gen directly transforms a MetadataTokenName into a CgMember /// $nfCgMems.Add($nfCgType.CgType.FindCgMemberByTokenName($_)) /// } /// /// #blow away the orphaned members from the original source code file(s) /// [NoFuture.Gen.RefactorExtensions]::RemoveMembers($nfCgMems, $true) /// } /// ]]> /// </example> public AssemblyAnalysis(bool resolveGacAsmNames, params int[] ports) { if (string.IsNullOrWhiteSpace(NfConfig.CustomTools.InvokeAssemblyAnalysis) || !File.Exists(NfConfig.CustomTools.InvokeAssemblyAnalysis)) { throw new ItsDeadJim("Don't know where to locate the NoFuture.Tokens.DotNetMeta.InvokeAssemblyAnalysis, assign " + "the global variable at NoFuture.Shared.Cfg.NfConfig.CustomTools.InvokeAssemblyAnalysis."); } var np = DefaultPort; var usePorts = new int[6]; for (var i = 0; i < usePorts.Length; i++) { usePorts[i] = ports != null && ports.Length >= i + 1 ? ports[i] : np + i; } var args = string.Join(" ", ConsoleCmd.ConstructCmdLineArgs(GET_ASM_INDICES_PORT_CMD_SWITCH, usePorts[0].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(GET_TOKEN_IDS_PORT_CMD_SWITCH, usePorts[1].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(GET_TOKEN_NAMES_PORT_CMD_SWITCH, usePorts[2].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(GET_TOKEN_PAGE_RANK_PORT_CMD_SWITCH, usePorts[3].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(GET_TOKEN_TYPES_PORT_CMD_SWITCH, usePorts[4].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(REASSIGN_TOKEN_NAMES_PORT_CMD_SWITCH, usePorts[5].ToString(CultureInfo.InvariantCulture)), ConsoleCmd.ConstructCmdLineArgs(RESOLVE_GAC_ASM_SWITCH, resolveGacAsmNames.ToString())); MyProcess = StartRemoteProcess(NfConfig.CustomTools.InvokeAssemblyAnalysis, args); _getAsmIndiciesCmd = new InvokeGetAsmIndicies { ProcessId = MyProcess.Id, SocketPort = usePorts[0] }; _getTokenIdsCmd = new InvokeGetTokenIds { ProcessId = MyProcess.Id, SocketPort = usePorts[1] }; _getTokenNamesCmd = new InvokeGetTokenNames { ProcessId = MyProcess.Id, SocketPort = usePorts[2] }; _getTokenPageRankCmd = new InvokeGetTokenPageRank { ProcessId = MyProcess.Id, SocketPort = usePorts[3] }; _getTokenTypesCmd = new InvokeGetTokenTypes { ProcessId = MyProcess.Id, SocketPort = usePorts[4] }; _reassignTokenNamesCmd = new InvokeReassignTokenNames { ProcessId = MyProcess.Id, SocketPort = usePorts[5] }; }
/// <summary> /// Runs its counterpart <see cref="GetCgOfType"/> in an isolated process. /// </summary> /// <param name="assemblyPath"></param> /// <param name="typeFullName"></param> /// <param name="resolveDependencies"></param> /// <param name="maxWaitInSeconds"></param> /// <returns></returns> public static CgType GetIsolatedCgOfType(string assemblyPath, string typeFullName, bool resolveDependencies = false, int maxWaitInSeconds = 60) { var tempDebug = NfConfig.TempDirectories.Debug; if (string.IsNullOrWhiteSpace(tempDebug) || !Directory.Exists(tempDebug)) { throw new RahRowRagee("The constant value at " + "'NoFuture.Shared.Cfg.NfConfig.TempDirectories.Debug' is not assigned."); } var invokeGetCgTypePath = NfConfig.CustomTools.InvokeGetCgType; if (string.IsNullOrWhiteSpace(typeFullName)) { throw new ArgumentNullException(nameof(typeFullName)); } if (string.IsNullOrWhiteSpace(invokeGetCgTypePath)) { throw new RahRowRagee("The constants value at " + "'NoFuture.Shared.Cfg.NfConfig.CustomTools.InvokeGetCgType' is not assigned."); } if (!File.Exists(invokeGetCgTypePath)) { throw new ItsDeadJim( $"The binary 'NoFuture.Gen.InvokeGetCgOfType.exe' is not at '{invokeGetCgTypePath}'."); } var argPath = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_ASM_PATH_SWITCH, assemblyPath); var argType = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_FULL_TYPE_NAME_SWITCH, typeFullName); var argResolve = ConsoleCmd.ConstructCmdLineArgs(Settings.INVOKE_RESOLVE_ALL_DEPENDENCIES, resolveDependencies.ToString()); string buffer = null; using (var invokeGetCgType = new Process { StartInfo = new ProcessStartInfo(invokeGetCgTypePath) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, Arguments = string.Join(" ", new[] { argPath, argType, argResolve }) } }) { invokeGetCgType.Start(); buffer = invokeGetCgType.StandardOutput.ReadToEnd(); invokeGetCgType.WaitForExit(maxWaitInSeconds); if (!invokeGetCgType.HasExited) { invokeGetCgType.Kill(); throw new ItsDeadJim( "The 'NoFuture.Gen.InvokeGetCgOfType.exe' ran " + $"longer than '{maxWaitInSeconds}' seconds and was shut down."); } } if (string.IsNullOrWhiteSpace(buffer)) { throw new ItsDeadJim("The invocation to 'NoFuture.Gen.InvokeGetCgOfType.exe' didn't " + "return anything on its standard output."); } File.WriteAllBytes(Path.Combine(tempDebug, "GetIsolatedCgType.json"), Encoding.UTF8.GetBytes(buffer)); var dcs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(CgType)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(buffer))) { var cgTypeOut = dcs.ReadObject(ms) as CgType; if (cgTypeOut == null) { throw new ItsDeadJim( $"Could not deserialize into a CgType from the standard output text of\n {buffer}"); } return(cgTypeOut); } }