Beispiel #1
0
        /// <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]
            };
        }
Beispiel #2
0
        /// <summary>
        /// Launches the NoFuture.Util.Gia.InvokeAssemblyAnalysis.exe and calls 
        /// GetAsmIndicies command.
        /// </summary>
        /// <param name="assemblyPath"></param>
        /// <param name="ports"></param>
        /// <param name="resolveGacAsmNames"></param>
        /// <returns></returns>
        /// <remarks>
        /// The ports used are defaulted to <see cref="DF_START_PORT"/>.
        /// </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.Util.Gia.AssemblyAnalysis
        /// (3) new process NoFuture.Util.Gia.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[
        ///  $myAsmAly = New-Object NoFuture.Util.Gia.AssemblyAnalysis($AssemblyPath,$false)
        ///  $myTokensIds = $myAsmAly.GetTokenIds(0, "NoFuture.*")
        ///  $myFlatTokens = $myTokensIds.FlattenToDistinct()
        ///  $myTokenNames = $myAsmAly.GetTokenNames($myFlatTokens)
        /// ]]>
        /// </example>
        public AssemblyAnalysis(string assemblyPath, bool resolveGacAsmNames, params int[] ports)
        {
            if (string.IsNullOrWhiteSpace(assemblyPath) || !File.Exists(assemblyPath))
                throw new ItsDeadJim("This isn't a valid assembly path");

            if (string.IsNullOrWhiteSpace(CustomTools.InvokeAssemblyAnalysis) || !File.Exists(CustomTools.InvokeAssemblyAnalysis))
                throw new ItsDeadJim("Don't know where to locate the NoFuture.Util.Gia.InvokeAssemblyAnalysis, assign " +
                                     "the global variable at NoFuture.CustomTools.InvokeAssemblyAnalysis.");

            var np = DF_START_PORT;
            var usePorts = new int[4];
            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(RESOLVE_GAC_ASM_SWITCH, resolveGacAsmNames.ToString()));

            MyProcess = StartRemoteProcess(CustomTools.InvokeAssemblyAnalysis, args);

            var getAsmIndicesCmd = new InvokeGetAsmIndicies()
            {
                ProcessId = MyProcess.Id,
                SocketPort = usePorts[0]
            };

            _asmIndices = getAsmIndicesCmd.Receive(assemblyPath);

            _getTokenIdsCmd = new InvokeGetTokenIds(_asmIndices)
            {
                ProcessId = MyProcess.Id,
                SocketPort = usePorts[1]
            };

            _getTokenNamesCmd = new InvokeGetTokenNames()
            {
                ProcessId = MyProcess.Id,
                SocketPort = usePorts[2]
            };

            _getTokenPageRankCmd = new InvokeGetTokenPageRank
            {
                ProcessId = MyProcess.Id,
                SocketPort = usePorts[3]
            };
        }