Esempio n. 1
0
        private static bool InternalTryParse <TOptions>(string[] args, ParserOptions parserOptions, out TOptions options, out Exception ex)
            where TOptions : new()
        {
            options = default;
            ex      = null;
            TypeArgumentInfo arguments = null;

            try
            {
                // build a list of properties for the type passed in.
                // this will throw for cases where the type is incorrecly annotated with attributes
                TypeHelpers.ScanTypeForProperties <TOptions>(out arguments);

                // before we do anything, let's expand the response files (if any).
                args = ExpandResponseFiles(args);

                // short circuit the request for help!
                if (args.Length == 1)
                {
                    if (args[0] == HelpGenerator.RequestShortHelpParameter || args[0] == "/?")
                    {
                        HelpGenerator.DisplayHelp(HelpFormat.Short, arguments, ColorScheme.Get());
                        ex = new HelpRequestedException();
                        return(false);
                    }
                    else if (args[0] == HelpGenerator.RequestLongHelpParameter)
                    {
                        HelpGenerator.DisplayHelp(HelpFormat.Full, arguments, ColorScheme.Get());
                        ex = new HelpRequestedException();
                        return(false);
                    }
                }

                // we have groups!
                if (!arguments.ArgumentGroups.ContainsKey(string.Empty))
                {
                    return(ParseCommandGroups(args, ref options, arguments, parserOptions));
                }

                // parse the arguments and build the options object
                options = InternalParse <TOptions>(args, 0, arguments.ArgumentGroups[string.Empty], parserOptions);
                return(true);
            }
            catch (Exception innerParserException)
            {
                ex = new ParserException(innerParserException.Message, innerParserException);
                if (parserOptions.LogParseErrorToConsole)
                {
                    string errorFormat = $"[{ColorScheme.Get().ErrorColor}!Error]: {{0}} {{1}}";
                    Colorizer.WriteLine(errorFormat, ex.Message, Environment.NewLine);

                    HelpGenerator.DisplayHelp(HelpFormat.Short, arguments, ColorScheme.Get());
                }
                return(false);
            }
        }
Esempio n. 2
0
        private static void DisplayDetailedHelp(TypeArgumentInfo type, IColors colors)
        {
            string exeName = Assembly.GetEntryAssembly()?.GetName()?.Name;

            Colorizer.WriteLine("Usage: ");

            foreach (var item in type.ArgumentGroups.Keys)
            {
                DisplayDetailedArgumentHelp(exeName, item, type.ArgumentGroups[item], colors);
            }
        }
Esempio n. 3
0
        private static void DisplayShortHelp(TypeArgumentInfo type)
        {
            string exeName = Assembly.GetEntryAssembly()?.GetName()?.Name;

            Colorizer.WriteLine("Usage: ");

            DisplayCommandLine(exeName, type);

            Colorizer.WriteLine(string.Empty);
            Colorizer.WriteLine("For detailed information run '[White!{0} --help]'.", exeName);
        }
Esempio n. 4
0
 private static void DisplayHelp(string helpFormat, TypeArgumentInfo arguments)
 {
     if (helpFormat == RequestShortHelpParameter || helpFormat == "/?")
     {
         DisplayShortHelp(arguments);
     }
     else if (helpFormat == RequestLongHelpParameter)
     {
         DisplayDetailedHelp(arguments);
     }
 }
Esempio n. 5
0
 private static void DisplayCommandLine(string exeName, TypeArgumentInfo type)
 {
     foreach (var group in type.ArgumentGroups)
     {
         Colorizer.Write(" [White!{0}.exe] ", exeName);
         if (!string.IsNullOrEmpty(group.Key))
         {
             Colorizer.Write($"[Green!{group.Key}] ");
         }
         DisplayCommandLine(group.Value);
     }
 }
Esempio n. 6
0
        private static void DisplayShortHelp(TypeArgumentInfo type, IColors colors)
        {
            string exeName = Assembly.GetEntryAssembly()?.GetName()?.Name;

            Colorizer.WriteLine("Usage: ");

            DisplayCommandLine(exeName, type, colors);

            Colorizer.WriteLine(string.Empty);
            string errorFormat = $"For detailed information run '[{colors.AssemblyNameColor}!{{0}} --help]'.";

            Colorizer.WriteLine(errorFormat, exeName);
        }
Esempio n. 7
0
        internal static void DisplayHelp(HelpFormat helpFormat, TypeArgumentInfo arguments, IColors colors)
        {
            switch (helpFormat)
            {
            case HelpFormat.Short:
                DisplayShortHelp(arguments, colors);
                break;

            case HelpFormat.Full:
                DisplayDetailedHelp(arguments, colors);
                break;
            }
        }
Esempio n. 8
0
 private static void DisplayCommandLine(string exeName, TypeArgumentInfo type, IColors colors)
 {
     foreach (var group in type.ArgumentGroups)
     {
         string assemblyNameFormat = $" [{colors.AssemblyNameColor}!{{0}}.exe] "; // " [White!{{0}}.exe] "
         Colorizer.Write(assemblyNameFormat, exeName);
         if (!string.IsNullOrEmpty(group.Key))
         {
             string groupFormat = $" [{colors.ArgumentGroupColor}!{{0}}] "; // " [Green!{{0}}] "
             Colorizer.Write(string.Format(groupFormat, group.Key));
         }
         DisplayCommandLine(group.Value, colors);
     }
 }
Esempio n. 9
0
        public static bool TryParse <TOptions>(string[] args, out TOptions options)
            where TOptions : new()
        {
            options = default(TOptions);

            TypeArgumentInfo arguments = null;

            try
            {
                // build a list of properties for the type passed in.
                // this will throw for cases where the type is incorrecly annotated with attributes
                TypeHelpers.ScanTypeForProperties <TOptions>(out arguments);

                // before we do anything, let's expand the response files (if any).
                args = ExpandResponseFiles(args);

                // short circuit the request for help!
                if (args.Length == 1)
                {
                    if (args[0] == HelpGenerator.RequestShortHelpParameter || args[0] == "/?")
                    {
                        HelpGenerator.DisplayHelp(HelpFormat.Short, arguments);
                        return(false);
                    }
                    else if (args[0] == HelpGenerator.RequestLongHelpParameter)
                    {
                        HelpGenerator.DisplayHelp(HelpFormat.Full, arguments);
                        return(false);
                    }
                }

                // we have groups!
                if (!arguments.ArgumentGroups.ContainsKey(string.Empty))
                {
                    return(ParseCommandGroups(args, ref options, arguments));
                }

                // parse the arguments and build the options object
                options = InternalParse <TOptions>(args, 0, arguments.ArgumentGroups[string.Empty]);
                return(true);
            }
            catch (Exception ex)
            {
                Colorizer.WriteLine($"[Red!Error]: {ex.Message} {Environment.NewLine}");

                HelpGenerator.DisplayHelp(HelpFormat.Short, arguments);

                return(false);
            }
        }
Esempio n. 10
0
        internal static void DisplayHelp(HelpFormat helpFormat, TypeArgumentInfo arguments)
        {
            switch (helpFormat)
            {
            case HelpFormat.Short:
                DisplayShortHelp(arguments);
                break;

            case HelpFormat.Full:
                DisplayDetailedHelp(arguments);
                break;

            default:
                throw new ArgumentException("Unrecognized help format", nameof(helpFormat));
            }
        }
Esempio n. 11
0
            internal TypeCache(MetadataType type, Logger?logger, ILProvider ilProvider)
            {
                Debug.Assert(type == type.GetTypeDefinition());
                Debug.Assert(!CompilerGeneratedNames.IsGeneratedMemberName(type.Name));

                Type = type;

                var callGraph          = new CompilerGeneratedCallGraph();
                var userDefinedMethods = new HashSet <MethodDesc>();

                void ProcessMethod(MethodDesc method)
                {
                    Debug.Assert(method == method.GetTypicalMethodDefinition());

                    bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(((MetadataType)method.OwningType).Name);

                    if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name))
                    {
                        if (!isStateMachineMember)
                        {
                            // If it's not a nested function, track as an entry point to the call graph.
                            var added = userDefinedMethods.Add(method);
                            Debug.Assert(added);
                        }
                    }
                    else
                    {
                        // We don't expect lambdas or local functions to be emitted directly into
                        // state machine types.
                        Debug.Assert(!isStateMachineMember);
                    }

                    // Discover calls or references to lambdas or local functions. This includes
                    // calls to local functions, and lambda assignments (which use ldftn).
                    var methodBody = ilProvider.GetMethodIL(method);

                    if (methodBody != null)
                    {
                        ILReader reader = new ILReader(methodBody.GetILBytes());
                        while (reader.HasNext)
                        {
                            ILOpcode opcode = reader.ReadILOpcode();
                            switch (opcode)
                            {
                            case ILOpcode.ldftn:
                            case ILOpcode.ldtoken:
                            case ILOpcode.call:
                            case ILOpcode.callvirt:
                            case ILOpcode.newobj:
                            {
                                MethodDesc?referencedMethod = methodBody.GetObject(reader.ReadILToken(), NotFoundBehavior.ReturnNull) as MethodDesc;
                                if (referencedMethod == null)
                                {
                                    continue;
                                }

                                referencedMethod = referencedMethod.GetTypicalMethodDefinition();

                                if (referencedMethod.IsConstructor &&
                                    referencedMethod.OwningType is MetadataType generatedType &&
                                    // Don't consider calls in the same type, like inside a static constructor
                                    method.OwningType != generatedType &&
                                    CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.Name))
                                {
                                    Debug.Assert(generatedType.IsTypeDefinition);

                                    // fill in null for now, attribute providers will be filled in later
                                    _generatedTypeToTypeArgumentInfo ??= new Dictionary <MetadataType, TypeArgumentInfo>();

                                    if (!_generatedTypeToTypeArgumentInfo.TryAdd(generatedType, new TypeArgumentInfo(method, null)))
                                    {
                                        var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo[generatedType].CreatingMethod;
                                        logger?.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithUserMethod, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), generatedType.GetDisplayName());
                                    }
                                    continue;
                                }

                                if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(referencedMethod.Name))
                                {
                                    continue;
                                }

                                if (isStateMachineMember)
                                {
                                    callGraph.TrackCall((MetadataType)method.OwningType, referencedMethod);
                                }
                                else
                                {
                                    callGraph.TrackCall(method, referencedMethod);
                                }
                            }
                            break;

                            case ILOpcode.stsfld:
                            {
                                // Same as above, but stsfld instead of a call to the constructor
                                FieldDesc?field = methodBody.GetObject(reader.ReadILToken()) as FieldDesc;
                                if (field == null)
                                {
                                    continue;
                                }

                                field = field.GetTypicalFieldDefinition();

                                if (field.OwningType is MetadataType generatedType &&
                                    // Don't consider field accesses in the same type, like inside a static constructor
                                    method.OwningType != generatedType &&
                                    CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.Name))
                                {
                                    Debug.Assert(generatedType.IsTypeDefinition);

                                    _generatedTypeToTypeArgumentInfo ??= new Dictionary <MetadataType, TypeArgumentInfo>();

                                    if (!_generatedTypeToTypeArgumentInfo.TryAdd(generatedType, new TypeArgumentInfo(method, null)))
                                    {
                                        // It's expected that there may be multiple methods associated with the same static closure environment.
                                        // All of these methods will substitute the same type arguments into the closure environment
                                        // (if it is generic). Don't warn.
                                    }
                                    continue;
                                }
                            }
                            break;

                            default:
                                reader.Skip(opcode);
                                break;
                            }
                        }
                    }

                    if (TryGetStateMachineType(method, out MetadataType? stateMachineType))
                    {
                        Debug.Assert(stateMachineType.ContainingType == type ||
                                     (CompilerGeneratedNames.IsGeneratedMemberName(stateMachineType.ContainingType.Name) &&
                                      stateMachineType.ContainingType.ContainingType == type));
                        Debug.Assert(stateMachineType == stateMachineType.GetTypeDefinition());

                        callGraph.TrackCall(method, stateMachineType);

                        _compilerGeneratedTypeToUserCodeMethod ??= new Dictionary <MetadataType, MethodDesc>();
                        if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method))
                        {
                            var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
                            logger?.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName());
                        }
                        // Already warned above if multiple methods map to the same type
                        // Fill in null for argument providers now, the real providers will be filled in later
                        _generatedTypeToTypeArgumentInfo ??= new Dictionary <MetadataType, TypeArgumentInfo>();
                        _generatedTypeToTypeArgumentInfo[stateMachineType] = new TypeArgumentInfo(method, null);
                    }
                }

                // Look for state machine methods, and methods which call local functions.
                foreach (MethodDesc method in type.GetMethods())
                {
                    ProcessMethod(method);
                }

                // Also scan compiler-generated state machine methods (in case they have calls to nested functions),
                // and nested functions inside compiler-generated closures (in case they call other nested functions).

                // State machines can be emitted into lambda display classes, so we need to go down at least two
                // levels to find calls from iterator nested functions to other nested functions. We just recurse into
                // all compiler-generated nested types to avoid depending on implementation details.

                foreach (var nestedType in GetCompilerGeneratedNestedTypes(type))
                {
                    foreach (var method in nestedType.GetMethods())
                    {
                        ProcessMethod(method);
                    }
                }

                // Now we've discovered the call graphs for calls to nested functions.
                // Use this to map back from nested functions to the declaring user methods.

                // Note: This maps all nested functions back to the user code, not to the immediately
                // declaring local function. The IL doesn't contain enough information in general for
                // us to determine the nesting of local functions and lambdas.

                // Note: this only discovers nested functions which are referenced from the user
                // code or its referenced nested functions. There is no reliable way to determine from
                // IL which user code an unused nested function belongs to.

                foreach (var userDefinedMethod in userDefinedMethods)
                {
                    var callees = callGraph.GetReachableMembers(userDefinedMethod);
                    if (!callees.Any())
                    {
                        continue;
                    }

                    _compilerGeneratedMembers ??= new Dictionary <MethodDesc, List <TypeSystemEntity> >();
                    _compilerGeneratedMembers.Add(userDefinedMethod, new List <TypeSystemEntity>(callees));

                    foreach (var compilerGeneratedMember in callees)
                    {
                        switch (compilerGeneratedMember)
                        {
                        case MethodDesc nestedFunction:
                            Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.Name));
                            // Nested functions get suppressions from the user method only.
                            _compilerGeneratedMethodToUserCodeMethod ??= new Dictionary <MethodDesc, MethodDesc>();
                            if (!_compilerGeneratedMethodToUserCodeMethod.TryAdd(nestedFunction, userDefinedMethod))
                            {
                                var alreadyAssociatedMethod = _compilerGeneratedMethodToUserCodeMethod[nestedFunction];
                                logger?.LogWarning(new MessageOrigin(userDefinedMethod), DiagnosticId.MethodsAreAssociatedWithUserMethod, userDefinedMethod.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), nestedFunction.GetDisplayName());
                            }
                            break;

                        case MetadataType stateMachineType:
                            // Types in the call graph are always state machine types
                            // For those all their methods are not tracked explicitly in the call graph; instead, they
                            // are represented by the state machine type itself.
                            // We are already tracking the association of the state machine type to the user code method
                            // above, so no need to track it here.
                            Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.Name));
                            break;

                        default:
                            throw new InvalidOperationException();
                        }
                    }
                }

                // Now that we have instantiating methods fully filled out, walk the generated types and fill in the attribute
                // providers
                if (_generatedTypeToTypeArgumentInfo != null)
                {
                    foreach (var generatedType in _generatedTypeToTypeArgumentInfo.Keys)
                    {
                        Debug.Assert(generatedType == generatedType.GetTypeDefinition());

                        if (HasGenericParameters(generatedType))
                        {
                            MapGeneratedTypeTypeParameters(generatedType);
                        }
                    }
                }
Esempio n. 12
0
        private static bool ParseCommandGroups <TOptions>(string[] args, ref TOptions options, TypeArgumentInfo arguments) where TOptions : new()
        {
            if (arguments.ActionArgument == null)
            {
                throw new ArgumentException("Cannot have groups unless Command argument has been specified");
            }

            if (args.Length == 0)
            {
                throw new ArgumentException("Required parameters have not been specified");
            }

            // parse based on the command passed in (the first arg).
            if (!arguments.ArgumentGroups.ContainsKey(args[0]))
            {
                throw new ArgumentException($"Unknown command [Cyan!{args[0]}]");
            }

            // short circuit the request for help!
            if (args.Length == 2 && (args[1] == "/?" || args[1] == "-?"))
            {
                HelpGenerator.DisplayHelpForCommmand(args[0], arguments.ArgumentGroups[args[0]]);
                return(false);
            }

            options = InternalParse <TOptions>(args, 1, arguments.ArgumentGroups[args[0]]);
            arguments.ActionArgument.SetValue(options, PropertyHelpers.GetValueAsType(args[0], arguments.ActionArgument));
            return(true);
        }