Example #1
0
        private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, string[] typeArguments, string[] argumentValues = null)
        {
            Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags,
                         "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags);

            var instructionDecoder = CSharpInstructionDecoder.Instance;
            var method             = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder);

            var includeParameterTypes     = argumentFlags.Includes(DkmVariableInfoFlags.Types);
            var includeParameterNames     = argumentFlags.Includes(DkmVariableInfoFlags.Names);
            ArrayBuilder <string> builder = null;

            if (argumentValues != null)
            {
                builder = ArrayBuilder <string> .GetInstance();

                builder.AddRange(argumentValues);
            }

            var name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder);

            builder?.Free();

            return(name);
        }
Example #2
0
            /// <summary>
            /// Updates the currently stored 'best result' if the current result is better.
            /// Returns 'true' if no further work is required and we can break early, or
            /// 'false' if we need to keep on going.
            ///
            /// If 'weight' is better than 'bestWeight' and matchSpanToAdd is not null, then
            /// matchSpanToAdd will be added to matchedSpansInReverse.
            /// </summary>
            private bool UpdateBestResultIfBetter(
                int?weight, ArrayBuilder <TextSpan> matchedSpansInReverse,
                ref int?bestWeight, ref ArrayBuilder <TextSpan> bestMatchedSpansInReverse,
                TextSpan?matchSpanToAdd)
            {
                if (!IsBetter(weight, bestWeight))
                {
                    // Even though we matched this current candidate hump we failed to match
                    // the remainder of the pattern.  Continue to the next candidate hump
                    // to see if our pattern character will match it and potentially succeed.
                    matchedSpansInReverse?.Free();

                    // We need to keep going.
                    return(false);
                }

                if (matchSpanToAdd != null)
                {
                    matchedSpansInReverse?.Add(matchSpanToAdd.Value);
                }

                // This was result was better than whatever previous best result we had was.
                // Free and overwrite the existing best results, and keep going.
                bestWeight = weight;
                bestMatchedSpansInReverse?.Free();
                bestMatchedSpansInReverse = matchedSpansInReverse;

                // We found a path that allowed us to match everything contiguously
                // from the beginning.  This is the best match possible.  So we can
                // just break out now and return this result.
                return(weight == CamelCaseMaxWeight);
            }
Example #3
0
        private static void FreeDeconstructionVariables(ArrayBuilder<DeconstructionVariable> variables)
        {
            foreach (var v in variables)
            {
                if (v.HasNestedVariables)
                {
                    FreeDeconstructionVariables(v.NestedVariables);
                }
            }

            variables.Free();
        }
Example #4
0
        private void GenerateImpl()
        {
            SetInitialDebugDocument();

            // Synthesized methods should have a sequence point
            // at offset 0 to ensure correct stepping behavior.
            if (_emitPdbSequencePoints && _method.IsImplicitlyDeclared)
            {
                _builder.DefineInitialHiddenSequencePoint();
            }

            try
            {
                EmitStatement(_boundBody);

                if (_indirectReturnState == IndirectReturnState.Needed)
                {
                    // it is unfortunate that return was not handled while we were in scope of the method
                    // it can happen in rare cases involving exception handling (for example all returns were from a try)
                    // in such case we can still handle return here.
                    HandleReturn();
                }

                if (!_diagnostics.HasAnyErrors())
                {
                    _builder.Realize();
                }
            }
            catch (EmitCancelledException)
            {
                Debug.Assert(_diagnostics.HasAnyErrors());
            }

            _synthesizedLocalOrdinals.Free();

            Debug.Assert(!(_expressionTemps?.Count > 0), "leaking expression temps?");
            _expressionTemps?.Free();
            _savedSequencePoints?.Free();
        }
        protected override DiagnosticInfo ResolveInfo()
        {
            var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance();

            var warningsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance();

            ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;

            // CheckTypeConstraints should only add nullability warnings to warningsBuilder.
            ConstraintsHelper.CheckTypeConstraints(
                _type,
                _conversions,
                _compilation,
                diagnosticsBuilder,
                warningsBuilder,
                ref useSiteDiagnosticsBuilder);
            // If there are multiple constraint check warnings, we'll report the first one only.
            var diagnostic = (warningsBuilder.Count == 0) ? null : warningsBuilder[0].DiagnosticInfo;

            useSiteDiagnosticsBuilder?.Free();
            warningsBuilder.Free();
            diagnosticsBuilder.Free();
            return(diagnostic);
        }
        private ImmutableArray <MergedNamespaceOrTypeDeclaration> MakeChildren()
        {
            ArrayBuilder <SingleNamespaceDeclaration> namespaces = null;
            ArrayBuilder <SingleTypeDeclaration>      types      = null;

            #region MyRegion
            ArrayBuilder <TemplateDeclaration> templates = null;
            #endregion
            bool allNamespacesHaveSameName = true;
            bool allTypesHaveSameIdentity  = true;

            foreach (var decl in _declarations)
            {
                foreach (var child in decl.Children)
                {
                    // it is either a type (more likely)
                    var asType = child as SingleTypeDeclaration;
                    if (asType != null)
                    {
                        // handle types
                        if (types == null)
                        {
                            types = ArrayBuilder <SingleTypeDeclaration> .GetInstance();
                        }
                        else if (allTypesHaveSameIdentity && !asType.Identity.Equals(types[0].Identity))
                        {
                            allTypesHaveSameIdentity = false;
                        }

                        types.Add(asType);
                        continue;
                    }

                    // or it is a namespace
                    var asNamespace = child as SingleNamespaceDeclaration;
                    if (asNamespace != null)
                    {
                        // handle namespace
                        if (namespaces == null)
                        {
                            namespaces = ArrayBuilder <SingleNamespaceDeclaration> .GetInstance();
                        }
                        else if (allNamespacesHaveSameName && !asNamespace.Name.Equals(namespaces[0].Name))
                        {
                            allNamespacesHaveSameName = false;
                        }

                        namespaces.Add(asNamespace);
                        continue;
                    }

                    #region PackageTemplate - MergedNamespaceDeclaration.MakeChildren()
                    // ...or a template?
                    var asTemplate = child as TemplateDeclaration;
                    if (asTemplate != null)
                    {
                        if (templates == null)
                        {
                            templates = ArrayBuilder <TemplateDeclaration> .GetInstance();
                        }

                        templates.Add(asTemplate);
                        continue;
                    }
                    #endregion

                    // Not sure if we can get here, perhaps, if we have errors,
                    // but we care only about types and namespaces anyways.
                }
            }

            var children = ArrayBuilder <MergedNamespaceOrTypeDeclaration> .GetInstance();

            if (namespaces != null)
            {
                if (allNamespacesHaveSameName)
                {
                    children.Add(MergedNamespaceDeclaration.Create(namespaces.ToImmutableAndFree()));
                }
                else
                {
                    var namespaceGroups = namespaces.ToDictionary(n => n.Name, StringOrdinalComparer.Instance);
                    namespaces.Free();

                    foreach (var namespaceGroup in namespaceGroups.Values)
                    {
                        children.Add(MergedNamespaceDeclaration.Create(namespaceGroup));
                    }
                }
            }

            if (types != null)
            {
                if (allTypesHaveSameIdentity)
                {
                    children.Add(new MergedTypeDeclaration(types.ToImmutableAndFree()));
                }
                else
                {
                    var typeGroups = types.ToDictionary(t => t.Identity);
                    types.Free();

                    foreach (var typeGroup in typeGroups.Values)
                    {
                        children.Add(new MergedTypeDeclaration(typeGroup));
                    }
                }
            }

            #region Package Template - MergedNamespaceDeclaration MakeChildren()
            if (templates != null)
            {
                foreach (var templateDeclaration in templates)
                {
                    children.Add(new MergedTemplateDeclaration(templateDeclaration));
                }

                templates.Free();
            }
            #endregion

            return(children.ToImmutableAndFree());
        }
Example #7
0
 public void Free()
 => MatchedSpansInReverse?.Free();
Example #8
0
 public void Dispose()
 {
     _spans?.Free();
 }
Example #9
0
            private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasInvalidOrDynamicOperation)
            {
                // We bail out reporting diagnostics for named types if it contains following kind of operations:
                //  1. Invalid operations, i.e. erroneous code:
                //     We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user
                //     is still editing code and fixing unresolved references to symbols, such as overload resolution errors.
                //  2. Dynamic operations, where we do not know the exact member being referenced at compile time.
                if (hasInvalidOrDynamicOperation)
                {
                    return;
                }

                if (symbolEndContext.Symbol.GetAttributes().Any(a => a.AttributeClass == _structLayoutAttributeType))
                {
                    // Bail out for types with 'StructLayoutAttribute' as the ordering of the members is critical,
                    // and removal of unused members might break semantics.
                    return;
                }

                // Report diagnostics for unused candidate members.
                var first = true;
                PooledHashSet <ISymbol> symbolsReferencedInDocComments    = null;
                ArrayBuilder <string>   debuggerDisplayAttributeArguments = null;

                try
                {
                    var namedType = (INamedTypeSymbol)symbolEndContext.Symbol;
                    foreach (var member in namedType.GetMembers())
                    {
                        // Check if the underlying member is neither read nor a readable reference to the member is taken.
                        // If so, we flag the member as either unused (never written) or unread (written but not read).
                        if (TryRemove(member, out var valueUsageInfo) &&
                            !valueUsageInfo.IsReadFrom())
                        {
                            Debug.Assert(IsCandidateSymbol(member));
                            Debug.Assert(!member.IsImplicitlyDeclared);

                            if (first)
                            {
                                // Bail out if there are syntax errors in any of the declarations of the containing type.
                                // Note that we check this only for the first time that we report an unused or unread member for the containing type.
                                if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken))
                                {
                                    return;
                                }

                                // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations.
                                // This set is computed once and used for all the iterations of the loop.
                                symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken);

                                // Compute the set of string arguments to DebuggerDisplay attributes applied to any symbol within the named type declaration.
                                // These strings may have an embedded reference to the symbol.
                                // This set is computed once and used for all the iterations of the loop.
                                debuggerDisplayAttributeArguments = GetDebuggerDisplayAttributeArguments(namedType);

                                first = false;
                            }

                            // Simple heuristic for members referenced in DebuggerDisplayAttribute's string argument:
                            // bail out if any of the DebuggerDisplay string arguments contains the member name.
                            // In future, we can consider improving this heuristic to parse the embedded expression
                            // and resolve symbol references.
                            if (debuggerDisplayAttributeArguments.Any(arg => arg.Contains(member.Name)))
                            {
                                continue;
                            }

                            // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not.
                            var rule = !valueUsageInfo.IsWrittenTo() && !valueUsageInfo.IsNameOnly() && !symbolsReferencedInDocComments.Contains(member)
                                ? s_removeUnusedMembersRule
                                : s_removeUnreadMembersRule;

                            // Do not flag write-only properties that are not read.
                            // Write-only properties are assumed to have side effects
                            // visible through other means than a property getter.
                            if (rule == s_removeUnreadMembersRule &&
                                member is IPropertySymbol property &&
                                property.IsWriteOnly)
                            {
                                continue;
                            }

                            // Most of the members should have a single location, except for partial methods.
                            // We report the diagnostic on the first location of the member.
                            var diagnostic = DiagnosticHelper.CreateWithMessage(
                                rule,
                                member.Locations[0],
                                rule.GetEffectiveSeverity(symbolEndContext.Compilation.Options),
                                additionalLocations: null,
                                properties: null,
                                GetMessage(rule, member));
                            symbolEndContext.ReportDiagnostic(diagnostic);
                        }
                    }
                }
                finally
                {
                    symbolsReferencedInDocComments?.Free();
                    debuggerDisplayAttributeArguments?.Free();
                }

                return;
            }
Example #10
0
        /// <summary>
        /// Determine if "type" inherits from or implements "baseType", ignoring constructed types, and dealing
        /// only with original types.
        /// </summary>
        private static bool InheritsFromOrImplementsIgnoringConstruction(
            this TypeSymbol type,
            NamedTypeSymbol baseType,
            CSharpCompilation compilation,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics,
            ConsList <TypeSymbol> basesBeingResolved = null)
        {
            Debug.Assert(type.IsDefinition);
            Debug.Assert(baseType.IsDefinition);

            PooledHashSet <NamedTypeSymbol> interfacesLookedAt = null;
            ArrayBuilder <NamedTypeSymbol>  baseInterfaces     = null;

            bool baseTypeIsInterface = baseType.IsInterface;

            if (baseTypeIsInterface)
            {
                interfacesLookedAt = PooledHashSet <NamedTypeSymbol> .GetInstance();

                baseInterfaces = ArrayBuilder <NamedTypeSymbol> .GetInstance();
            }

            PooledHashSet <NamedTypeSymbol> visited = null;
            var  current = type;
            bool result  = false;

            while ((object)current != null)
            {
                Debug.Assert(current.IsDefinition);
                if (baseTypeIsInterface == current.IsInterfaceType() &&
                    current == (object)baseType)
                {
                    result = true;
                    break;
                }

                if (baseTypeIsInterface)
                {
                    getBaseInterfaces(current, baseInterfaces, interfacesLookedAt, basesBeingResolved);
                }

                // NOTE(cyrusn): The base type of an 'original' type may not be 'original'. i.e.
                // "class Goo : IBar<int>".  We must map it back to the 'original' when as we walk up
                // the base type hierarchy.
                var next = current.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited);
                if ((object)next == null)
                {
                    current = null;
                }
                else
                {
                    current = (TypeSymbol)next.OriginalDefinition;
                    current.AddUseSiteDiagnostics(ref useSiteDiagnostics);
                }
            }

            visited?.Free();

            if (!result && baseTypeIsInterface)
            {
                Debug.Assert(!result);

                while (baseInterfaces.Count != 0)
                {
                    NamedTypeSymbol currentBase = baseInterfaces.Pop();

                    if (!currentBase.IsInterface)
                    {
                        continue;
                    }

                    Debug.Assert(currentBase.IsDefinition);
                    if (currentBase == (object)baseType)
                    {
                        result = true;
                        break;
                    }

                    getBaseInterfaces(currentBase, baseInterfaces, interfacesLookedAt, basesBeingResolved);
                }

                if (!result)
                {
                    foreach (var candidate in interfacesLookedAt)
                    {
                        candidate.AddUseSiteDiagnostics(ref useSiteDiagnostics);
                    }
                }
            }

            interfacesLookedAt?.Free();
            baseInterfaces?.Free();
            return(result);
Example #11
0
        /// <summary>
        /// Given a list of viable lookup results (based on the name, arity, and containing symbol),
        /// attempt to select one.
        /// </summary>
        private ImmutableArray <Symbol> ProcessCrefMemberLookupResults(
            ImmutableArray <Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            BaseCrefParameterListSyntax parameterListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(!symbols.IsEmpty);

            if (parameterListSyntax == null)
            {
                return(ProcessParameterlessCrefMemberLookupResults(symbols, arity, memberSyntax, typeArgumentListSyntax, out ambiguityWinner, diagnostics));
            }

            ArrayBuilder <Symbol> candidates = ArrayBuilder <Symbol> .GetInstance();

            GetCrefOverloadResolutionCandidates(symbols, arity, typeArgumentListSyntax, candidates);

            ImmutableArray <ParameterSymbol> parameterSymbols = BindCrefParameters(parameterListSyntax, diagnostics);
            ImmutableArray <Symbol>          results          = PerformCrefOverloadResolution(candidates, parameterSymbols, arity, memberSyntax, out ambiguityWinner, diagnostics);

            candidates.Free();

            // NOTE: This diagnostic is just a hint that might help fix a broken cref, so don't do
            // any work unless there are no viable candidates.
            if (results.Length == 0)
            {
                for (int i = 0; i < parameterSymbols.Length; i++)
                {
                    if (ContainsNestedTypeOfUnconstructedGenericType(parameterSymbols[i].Type))
                    {
                        // This warning is new in Roslyn, because our better-defined semantics for
                        // cref lookup disallow some things that were possible in dev12.
                        //
                        // Consider the following code:
                        //
                        //   public class C<T>
                        //   {
                        //       public class Inner { }
                        //
                        //       public void M(Inner i) { }
                        //
                        //       /// <see cref="M"/>
                        //       /// <see cref="C{T}.M"/>
                        //       /// <see cref="C{Q}.M"/>
                        //       /// <see cref="C{Q}.M(C{Q}.Inner)"/>
                        //       /// <see cref="C{Q}.M(Inner)"/> // WRN_UnqualifiedNestedTypeInCref
                        //       public void N() { }
                        //   }
                        //
                        // Dev12 binds all of the crefs as "M:C`1.M(C{`0}.Inner)".
                        // Roslyn accepts all but the last.  The issue is that the context for performing
                        // the lookup is not C<Q>, but C<T>.  Consequently, Inner binds to C<T>.Inner and
                        // then overload resolution fails because C<T>.Inner does not match C<Q>.Inner,
                        // the parameter type of C<Q>.M.  Since we could not agree that the old behavior
                        // was desirable (other than for backwards compatibility) and since mimicking it
                        // would have been expensive, we settled on introducing a new warning that at
                        // least hints to the user how then can work around the issue (i.e. by qualifying
                        // Inner as C{Q}.Inner).  Additional details are available in DevDiv #743425.
                        //
                        // CONSIDER: We could actually put the qualified form in the warning message,
                        // but that would probably just make it more frustrating (i.e. if the compiler
                        // knows exactly what I mean, why do I have to type it).
                        //
                        // NOTE: This is not a great location (whole parameter instead of problematic type),
                        // but it's better than nothing.
                        diagnostics.Add(ErrorCode.WRN_UnqualifiedNestedTypeInCref, parameterListSyntax.Parameters[i].Location);
                        break;
                    }
                }
            }

            return(results);
        }
Example #12
0
 public void Dispose()
 => _buffer?.Free();
Example #13
0
        private SyntaxTriviaList RewriteTrivia(
            SyntaxTriviaList triviaList,
            int depth,
            bool isTrailing,
            bool indentAfterLineBreak,
            bool mustHaveSeparator,
            int lineBreaksAfter)
        {
            ArrayBuilder <SyntaxTrivia> currentTriviaList = ArrayBuilder <SyntaxTrivia> .GetInstance(triviaList.Count);

            try
            {
                foreach (var trivia in triviaList)
                {
                    if (trivia.IsKind(SyntaxKind.WhitespaceTrivia) ||
                        trivia.IsKind(SyntaxKind.EndOfLineTrivia) ||
                        trivia.FullWidth == 0)
                    {
                        continue;
                    }

                    var needsSeparator =
                        (currentTriviaList.Count > 0 && NeedsSeparatorBetween(currentTriviaList.Last())) ||
                        (currentTriviaList.Count == 0 && isTrailing);

                    var needsLineBreak = NeedsLineBreakBefore(trivia, isTrailing) ||
                                         (currentTriviaList.Count > 0 && NeedsLineBreakBetween(currentTriviaList.Last(), trivia, isTrailing));

                    if (needsLineBreak && !_afterLineBreak)
                    {
                        currentTriviaList.Add(GetCarriageReturnLineFeed());
                        _afterLineBreak   = true;
                        _afterIndentation = false;
                    }

                    if (_afterLineBreak)
                    {
                        if (!_afterIndentation && NeedsIndentAfterLineBreak(trivia))
                        {
                            currentTriviaList.Add(this.GetIndentation(GetDeclarationDepth(trivia)));
                            _afterIndentation = true;
                        }
                    }
                    else if (needsSeparator)
                    {
                        currentTriviaList.Add(GetSpace());
                        _afterLineBreak   = false;
                        _afterIndentation = false;
                    }

                    if (trivia.HasStructure)
                    {
                        var tr = this.VisitStructuredTrivia(trivia);
                        currentTriviaList.Add(tr);
                    }
                    else if (trivia.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia))
                    {
                        // recreate exterior to remove any leading whitespace
                        currentTriviaList.Add(s_trimmedDocCommentExtertior);
                    }
                    else
                    {
                        currentTriviaList.Add(trivia);
                    }

                    if (NeedsLineBreakAfter(trivia, isTrailing) &&
                        (currentTriviaList.Count == 0 || !EndsInLineBreak(currentTriviaList.Last())))
                    {
                        currentTriviaList.Add(GetCarriageReturnLineFeed());
                        _afterLineBreak   = true;
                        _afterIndentation = false;
                    }
                }

                if (lineBreaksAfter > 0)
                {
                    if (currentTriviaList.Count > 0 &&
                        EndsInLineBreak(currentTriviaList.Last()))
                    {
                        lineBreaksAfter--;
                    }

                    for (int i = 0; i < lineBreaksAfter; i++)
                    {
                        currentTriviaList.Add(GetCarriageReturnLineFeed());
                        _afterLineBreak   = true;
                        _afterIndentation = false;
                    }
                }
                else if (indentAfterLineBreak && _afterLineBreak && !_afterIndentation)
                {
                    currentTriviaList.Add(this.GetIndentation(depth));
                    _afterIndentation = true;
                }
                else if (mustHaveSeparator)
                {
                    currentTriviaList.Add(GetSpace());
                    _afterLineBreak   = false;
                    _afterIndentation = false;
                }

                if (currentTriviaList.Count == 0)
                {
                    return(default(SyntaxTriviaList));
                }
                else if (currentTriviaList.Count == 1)
                {
                    return(SyntaxFactory.TriviaList(currentTriviaList.First()));
                }
                else
                {
                    return(SyntaxFactory.TriviaList(currentTriviaList));
                }
            }
            finally
            {
                currentTriviaList.Free();
            }
        }
        private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps)
        {
            var receiverOpt = indexerAccess.ReceiverOpt;

            Debug.Assert(receiverOpt != null);

            BoundExpression transformedReceiver;

            if (CanChangeValueBetweenReads(receiverOpt))
            {
                BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                BoundAssignmentOperator assignmentToTemp;

                // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable
                // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                // SPEC VIOLATION: as value types.
                var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                transformedReceiver = receiverTemp;
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);
            }
            else
            {
                transformedReceiver = VisitExpression(receiverOpt);
            }

            // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
            // we have to preserve both the source-code order of the side effects and the side effects
            // only being executed once.
            //
            // This is a subtly different problem than the problem faced by the conventional call
            // rewriter; with the conventional call rewriter we already know that the side effects
            // will only be executed once because the arguments are only being pushed on the stack once.
            // In a compound equality operator on an indexer the indices are placed on the stack twice.
            // That is to say, if you have:
            //
            // C().M(z : Z(), x : X(), y : Y())
            //
            // then we can rewrite that into
            //
            // tempc = C()
            // tempz = Z()
            // tempc.M(X(), Y(), tempz)
            //
            // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
            // temporaries in
            //
            // C().Collection[z : Z(), x : X(), y : Y()] += 1;
            //
            // because we have to ensure not just that Z() happens first, but in addition that X() and Y() are only
            // called once.  We have to generate this as
            //
            // tempc = C().Collection
            // tempz = Z()
            // tempx = X()
            // tempy = Y()
            // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
            //
            // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
            // However, we can still do the optimization where constants are not stored in
            // temporaries; if we have
            //
            // C().Collection[z : 123, y : Y(), x : X()] += 1;
            //
            // Then we can generate that as
            //
            // tempc = C().Collection
            // tempx = X()
            // tempy = Y()
            // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;

            ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

            SyntaxNode               syntax           = indexerAccess.Syntax;
            PropertySymbol           indexer          = indexerAccess.Indexer;
            ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
            bool expanded = indexerAccess.Expanded;
            ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

            ImmutableArray <ParameterSymbol> parameters = indexer.Parameters;

            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

            ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(
                expanded,
                argsToParamsOpt,
                parameters,
                argumentRefKinds,
                rewrittenArguments,
                forceLambdaSpilling: true, // lambdas must produce exactly one delegate so they must be spilled into a temp
                actualArguments,
                refKinds,
                storesToTemps);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                BoundExpression         array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                BoundAssignmentOperator storeToTemp;
                var boundTemp = _factory.StoreToTemp(array, out storeToTemp);
                stores.Add(storeToTemp);
                temps.Add(boundTemp.LocalSymbol);
                actualArguments[actualArguments.Length - 1] = boundTemp;
            }

            // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in
            // compound assignments, but for deconstructions we use the setter if the getter is missing.)
            var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod();

            InsertMissingOptionalArguments(syntax, accessor.Parameters, actualArguments, refKinds);

            // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
            // duplicate side-effects, so we'll skip that step.

            rewrittenArguments = actualArguments.AsImmutableOrNull();

            foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
            {
                temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                stores.Add(tempAssignment);
            }

            storesToTemps.Free();
            argumentRefKinds = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            // This is a temporary object that will be rewritten away before the lowering completes.
            return(new BoundIndexerAccess(
                       syntax,
                       transformedReceiver,
                       indexer,
                       rewrittenArguments,
                       default(ImmutableArray <string>),
                       argumentRefKinds,
                       false,
                       default(ImmutableArray <int>),
                       null,
                       indexerAccess.UseSetterForDefaultArgumentGeneration,
                       indexerAccess.Type));
        }
Example #15
0
 public void Dispose()
 {
     Debug.Assert(_stack != null);
     _stack.Free();
     _stack = null;
 }
 public void Dispose()
 => _complexItemBuilder.Free();
Example #17
0
        public override BoundNode VisitTypeOrInstanceInitializers(BoundTypeOrInstanceInitializers node)
        {
            ImmutableArray <BoundStatement> originalStatements = node.Statements;
            ArrayBuilder <BoundStatement>   statements         = ArrayBuilder <BoundStatement> .GetInstance(node.Statements.Length);

            foreach (var initializer in originalStatements)
            {
                if (IsFieldOrPropertyInitializer(initializer))
                {
                    statements.Add(RewriteExpressionStatement((BoundExpressionStatement)initializer, suppressInstrumentation: true));
                }
                else
                {
                    statements.Add(VisitStatement(initializer));
                }
            }

            int  optimizedInitializers = 0;
            bool optimize = _compilation.Options.OptimizationLevel == OptimizationLevel.Release;

            for (int i = 0; i < statements.Count; i++)
            {
                if (statements[i] == null || (optimize && IsFieldOrPropertyInitializer(originalStatements[i]) && ShouldOptimizeOutInitializer(statements[i])))
                {
                    optimizedInitializers++;
                    if (!_factory.CurrentMethod.IsStatic)
                    {
                        // NOTE: Dev11 removes static initializers if ONLY all of them are optimized out
                        statements[i] = null;
                    }
                }
            }

            ImmutableArray <BoundStatement> rewrittenStatements;

            if (optimizedInitializers == statements.Count)
            {
                // all are optimized away
                rewrittenStatements = ImmutableArray <BoundStatement> .Empty;
                statements.Free();
            }
            else
            {
                // instrument remaining statements
                int remaining = 0;
                for (int i = 0; i < statements.Count; i++)
                {
                    BoundStatement rewritten = statements[i];

                    if (rewritten != null)
                    {
                        if (IsFieldOrPropertyInitializer(originalStatements[i]))
                        {
                            var original = (BoundExpressionStatement)originalStatements[i];
                            if (Instrument && !original.WasCompilerGenerated)
                            {
                                rewritten = _instrumenter.InstrumentFieldOrPropertyInitializer(original, rewritten);
                            }
                        }

                        statements[remaining] = rewritten;
                        remaining++;
                    }
                }

                statements.Count    = remaining;
                rewrittenStatements = statements.ToImmutableAndFree();
            }

            return(new BoundStatementList(node.Syntax, rewrittenStatements, node.HasErrors));
        }
        /// <summary>
        /// Get the import strings for a given method, following forward pointers as necessary.
        /// </summary>
        /// <returns>
        /// For each namespace enclosing the method, a list of import strings, innermost to outermost.
        /// There should always be at least one entry, for the global namespace.
        /// </returns>
        public static ImmutableArray <ImmutableArray <string> > GetCSharpGroupedImportStrings <TArg>(
            int methodToken,
            TArg arg,
            Func <int, TArg, byte[]> getMethodCustomDebugInfo,
            Func <int, TArg, ImmutableArray <string> > getMethodImportStrings,
            out ImmutableArray <string> externAliasStrings)
        {
            externAliasStrings = default(ImmutableArray <string>);

            ImmutableArray <short> groupSizes = default(ImmutableArray <short>);
            bool seenForward = false;

RETRY:
            byte[] bytes = getMethodCustomDebugInfo(methodToken, arg);
            if (bytes == null)
            {
                return(default(ImmutableArray <ImmutableArray <string> >));
            }

            foreach (CustomDebugInfoRecord record in GetCustomDebugInfoRecords(bytes))
            {
                switch (record.Kind)
                {
                case CustomDebugInfoKind.UsingInfo:
                    if (!groupSizes.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Expected at most one Using record for method {0}", FormatMethodToken(methodToken)));
                    }

                    groupSizes = DecodeUsingRecord(record.Data);
                    break;

                case CustomDebugInfoKind.ForwardInfo:
                    if (!externAliasStrings.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Did not expect both Forward and ForwardToModule records for method {0}", FormatMethodToken(methodToken)));
                    }

                    methodToken = DecodeForwardRecord(record.Data);

                    // Follow at most one forward link (as in FUNCBRECEE::ensureNamespaces).
                    // NOTE: Dev11 may produce chains of forward links (e.g. for System.Collections.Immutable).
                    if (!seenForward)
                    {
                        seenForward = true;
                        goto RETRY;
                    }

                    break;

                case CustomDebugInfoKind.ForwardToModuleInfo:
                    if (!externAliasStrings.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Expected at most one ForwardToModule record for method {0}", FormatMethodToken(methodToken)));
                    }

                    int moduleInfoMethodToken = DecodeForwardToModuleRecord(record.Data);

                    ImmutableArray <string> allModuleInfoImportStrings = getMethodImportStrings(moduleInfoMethodToken, arg);
                    Debug.Assert(!allModuleInfoImportStrings.IsDefault);

                    ArrayBuilder <string> externAliasBuilder = ArrayBuilder <string> .GetInstance();

                    foreach (string importString in allModuleInfoImportStrings)
                    {
                        if (IsCSharpExternAliasInfo(importString))
                        {
                            externAliasBuilder.Add(importString);
                        }
                    }

                    externAliasStrings = externAliasBuilder.ToImmutableAndFree();
                    break;
                }
            }

            if (groupSizes.IsDefault)
            {
                // This can happen in malformed PDBs (e.g. chains of forwards).
                return(default(ImmutableArray <ImmutableArray <string> >));
            }

            ImmutableArray <string> importStrings = getMethodImportStrings(methodToken, arg);

            Debug.Assert(!importStrings.IsDefault);

            ArrayBuilder <ImmutableArray <string> > resultBuilder = ArrayBuilder <ImmutableArray <string> > .GetInstance(groupSizes.Length);

            ArrayBuilder <string> groupBuilder = ArrayBuilder <string> .GetInstance();

            int pos = 0;

            foreach (short groupSize in groupSizes)
            {
                for (int i = 0; i < groupSize; i++, pos++)
                {
                    if (pos >= importStrings.Length)
                    {
                        throw new InvalidOperationException(string.Format("Group size indicates more imports than there are import strings (method {0}).", FormatMethodToken(methodToken)));
                    }

                    string importString = importStrings[pos];
                    if (IsCSharpExternAliasInfo(importString))
                    {
                        throw new InvalidOperationException(string.Format("Encountered extern alias info before all import strings were consumed (method {0}).", FormatMethodToken(methodToken)));
                    }

                    groupBuilder.Add(importString);
                }

                resultBuilder.Add(groupBuilder.ToImmutable());
                groupBuilder.Clear();
            }

            if (externAliasStrings.IsDefault)
            {
                Debug.Assert(groupBuilder.Count == 0);

                // Extern alias detail strings (prefix "Z") are not included in the group counts.
                for (; pos < importStrings.Length; pos++)
                {
                    string importString = importStrings[pos];
                    if (!IsCSharpExternAliasInfo(importString))
                    {
                        throw new InvalidOperationException(string.Format("Expected only extern alias info strings after consuming the indicated number of imports (method {0}).", FormatMethodToken(methodToken)));
                    }

                    groupBuilder.Add(importString);
                }

                externAliasStrings = groupBuilder.ToImmutableAndFree();
            }
            else
            {
                groupBuilder.Free();

                if (pos < importStrings.Length)
                {
                    throw new InvalidOperationException(string.Format("Group size indicates fewer imports than there are import strings (method {0}).", FormatMethodToken(methodToken)));
                }
            }

            return(resultBuilder.ToImmutableAndFree());
        }
        /// <summary>
        /// Get the import strings for a given method, following forward pointers as necessary.
        /// </summary>
        /// <returns>
        /// For each namespace enclosing the method, a list of import strings, innermost to outermost.
        /// There should always be at least one entry, for the global namespace.
        /// </returns>
        public static ImmutableArray <ImmutableArray <string> > GetCSharpGroupedImportStrings(this ISymUnmanagedReader reader, int methodToken, out ImmutableArray <string> externAliasStrings)
        {
            externAliasStrings = default(ImmutableArray <string>);

            ImmutableArray <short> groupSizes = default(ImmutableArray <short>);
            bool seenForward = false;

RETRY:
            var bytes = reader.GetCustomDebugInfo(methodToken);

            if (bytes == null)
            {
                return(default(ImmutableArray <ImmutableArray <string> >));
            }

            int offset = 0;

            byte globalVersion;
            byte unusedGlobalCount;

            ReadGlobalHeader(bytes, ref offset, out globalVersion, out unusedGlobalCount);
            CheckVersion(globalVersion, methodToken);

            while (offset < bytes.Length)
            {
                byte version;
                CustomDebugInfoKind kind;
                int size;
                ReadRecordHeader(bytes, ref offset, out version, out kind, out size);
                CheckVersion(version, methodToken);

                if (kind == CustomDebugInfoKind.UsingInfo)
                {
                    if (!groupSizes.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Expected at most one Using record for method {0}", FormatMethodToken(methodToken)));
                    }

                    ReadUsingRecord(bytes, ref offset, size, out groupSizes);
                }
                else if (kind == CustomDebugInfoKind.ForwardInfo)
                {
                    if (!externAliasStrings.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Did not expect both Forward and ForwardToModule records for method {0}", FormatMethodToken(methodToken)));
                    }

                    ReadForwardRecord(bytes, ref offset, size, out methodToken);
                    if (!seenForward) // Follow at most one forward link.
                    {
                        seenForward = true;
                        goto RETRY;
                    }
                }
                else if (kind == CustomDebugInfoKind.ForwardToModuleInfo)
                {
                    if (!externAliasStrings.IsDefault)
                    {
                        throw new InvalidOperationException(string.Format("Expected at most one ForwardToModule record for method {0}", FormatMethodToken(methodToken)));
                    }

                    int moduleInfoMethodToken;
                    ReadForwardToModuleRecord(bytes, ref offset, size, out moduleInfoMethodToken);
                    ImmutableArray <string> allModuleInfoImportStrings = GetImportStrings(reader.GetBaselineMethod(moduleInfoMethodToken));
                    ArrayBuilder <string>   externAliasBuilder         = ArrayBuilder <string> .GetInstance();

                    foreach (string importString in allModuleInfoImportStrings)
                    {
                        if (IsCSharpExternAliasInfo(importString))
                        {
                            externAliasBuilder.Add(importString);
                        }
                    }
                    externAliasStrings = externAliasBuilder.ToImmutableAndFree();
                }
                else
                {
                    SkipRecord(bytes, ref offset, size);
                }
            }

            if (groupSizes.IsDefault)
            {
                throw new InvalidOperationException(string.Format("Didn't find usings info for method {0}", FormatMethodToken(methodToken)));
            }

            ImmutableArray <string> importStrings = GetImportStrings(reader.GetBaselineMethod(methodToken));
            int numImportStrings = importStrings.Length;

            ArrayBuilder <ImmutableArray <string> > resultBuilder = ArrayBuilder <ImmutableArray <string> > .GetInstance(groupSizes.Length);

            ArrayBuilder <string> groupBuilder = ArrayBuilder <string> .GetInstance();

            int pos = 0;

            foreach (short groupSize in groupSizes)
            {
                for (int i = 0; i < groupSize; i++, pos++)
                {
                    if (pos >= numImportStrings)
                    {
                        throw new InvalidOperationException(string.Format("Group size indicates more imports than there are import strings (method {0}).", FormatMethodToken(methodToken)));
                    }

                    string importString = importStrings[pos];
                    if (IsCSharpExternAliasInfo(importString))
                    {
                        throw new InvalidOperationException(string.Format("Encountered extern alias info before all import strings were consumed (method {0}).", FormatMethodToken(methodToken)));
                    }

                    groupBuilder.Add(importString);
                }

                resultBuilder.Add(groupBuilder.ToImmutable());
                groupBuilder.Clear();
            }


            if (externAliasStrings.IsDefault)
            {
                Debug.Assert(groupBuilder.Count == 0);

                // Extern alias detail strings (prefix "Z") are not included in the group counts.
                for (; pos < numImportStrings; pos++)
                {
                    string importString = importStrings[pos];
                    if (!IsCSharpExternAliasInfo(importString))
                    {
                        throw new InvalidOperationException(string.Format("Expected only extern alias info strings after consuming the indicated number of imports (method {0}).", FormatMethodToken(methodToken)));
                    }

                    groupBuilder.Add(importString);
                }

                externAliasStrings = groupBuilder.ToImmutableAndFree();
            }
            else
            {
                groupBuilder.Free();

                if (pos < numImportStrings)
                {
                    throw new InvalidOperationException(string.Format("Group size indicates fewer imports than there are import strings (method {0}).", FormatMethodToken(methodToken)));
                }
            }

            return(resultBuilder.ToImmutableAndFree());
        }
            private ParameterInfo[] ParseParameterList()
            {
                // Consume the opening parenthesis or bracket
                Debug.Assert(PeekNextChar() == '(' || PeekNextChar() == '[');
                ++_index;

                var nextChar = PeekNextChar();
                if (nextChar == ')' || nextChar == ']')
                {
                    // Empty parameter list
                    ++_index;
                    return s_noParameters;
                }

                var builder = new ArrayBuilder<ParameterInfo>();

                while (true)
                {
                    var parameter = ParseParameter();
                    if (parameter != null)
                    {
                        builder.Add(parameter.Value);
                    }
                    else
                    {
                        builder.Free();
                        return null;
                    }

                    if (PeekNextChar() == ',')
                    {
                        ++_index;
                    }
                    else
                    {
                        break;
                    }
                }

                nextChar = PeekNextChar();
                if (nextChar == ')' || nextChar == ']')
                {
                    // Consume the closing parenthesis or bracket
                    ++_index;
                }
                else
                {
                    // Malformed parameter list: missing close parenthesis or bracket
                    builder.Free();
                    return null;
                }

                return builder.ToArrayAndFree();
            }
        /// <summary>
        /// The strategy of this rewrite is to do rewrite "locally".
        /// We analyze arguments of the concat in a shallow fashion assuming that
        /// lowering and optimizations (including this one) is already done for the arguments.
        /// Based on the arguments we select the most appropriate pattern for the current node.
        ///
        /// NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only
        ///       local information - i.e. we look at the arguments, but we do not know about siblings.
        ///       When we move to the parent, the node may be rewritten by this or some another optimization.
        ///
        /// Example:
        ///     result = ( "abc" + "def" + null ?? expr1 + "moo" + "baz" ) + expr2
        ///
        /// Will rewrite into:
        ///     result = Concat("abcdef", expr2)
        ///
        /// However there will be transient nodes like  Concat(expr1 + "moo")  that will not be present in the
        /// resulting tree.
        ///
        /// </summary>
        private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type)
        {
            Debug.Assert(
                operatorKind == BinaryOperatorKind.StringConcatenation ||
                operatorKind == BinaryOperatorKind.StringAndObjectConcatenation ||
                operatorKind == BinaryOperatorKind.ObjectAndStringConcatenation);

            if (_inExpressionLambda)
            {
                return(RewriteStringConcatInExpressionLambda(syntax, operatorKind, loweredLeft, loweredRight, type));
            }

            // Convert both sides to a string (calling ToString if necessary)
            loweredLeft  = ConvertConcatExprToString(syntax, loweredLeft);
            loweredRight = ConvertConcatExprToString(syntax, loweredRight);

            Debug.Assert(loweredLeft.Type.IsStringType() || loweredLeft.ConstantValue?.IsNull == true || loweredLeft.Type.IsErrorType());
            Debug.Assert(loweredRight.Type.IsStringType() || loweredRight.ConstantValue?.IsNull == true || loweredRight.Type.IsErrorType());

            // try fold two args without flattening.
            var folded = TryFoldTwoConcatOperands(syntax, loweredLeft, loweredRight);

            if (folded != null)
            {
                return(folded);
            }

            // flatten and merge -  ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2)
            ArrayBuilder <BoundExpression> leftFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <BoundExpression> rightFlattened = ArrayBuilder <BoundExpression> .GetInstance();

            FlattenConcatArg(loweredLeft, leftFlattened);
            FlattenConcatArg(loweredRight, rightFlattened);

            if (leftFlattened.Any() && rightFlattened.Any())
            {
                folded = TryFoldTwoConcatOperands(syntax, leftFlattened.Last(), rightFlattened.First());
                if (folded != null)
                {
                    rightFlattened[0] = folded;
                    leftFlattened.RemoveLast();
                }
            }

            leftFlattened.AddRange(rightFlattened);
            rightFlattened.Free();

            BoundExpression result;

            switch (leftFlattened.Count)
            {
            case 0:
                result = _factory.StringLiteral(string.Empty);
                break;

            case 1:
                // All code paths which reach here (through TryFoldTwoConcatOperands) have already called
                // RewriteStringConcatenationOneExpr if necessary
                result = leftFlattened[0];
                break;

            case 2:
                var left  = leftFlattened[0];
                var right = leftFlattened[1];
                result = RewriteStringConcatenationTwoExprs(syntax, left, right);
                break;

            case 3:
            {
                var first  = leftFlattened[0];
                var second = leftFlattened[1];
                var third  = leftFlattened[2];
                result = RewriteStringConcatenationThreeExprs(syntax, first, second, third);
            }
            break;

            case 4:
            {
                var first  = leftFlattened[0];
                var second = leftFlattened[1];
                var third  = leftFlattened[2];
                var fourth = leftFlattened[3];
                result = RewriteStringConcatenationFourExprs(syntax, first, second, third, fourth);
            }
            break;

            default:
                result = RewriteStringConcatenationManyExprs(syntax, leftFlattened.ToImmutable());
                break;
            }

            leftFlattened.Free();
            return(result);
        }
            private TypeInfo[] ParseTypeArgumentList(ISymbol bindingContext)
            {
                Debug.Assert(PeekNextChar() == '<');
                ++_index;

                var builder = new ArrayBuilder<TypeInfo>();

                while (true)
                {
                    var type = ParseType(bindingContext);
                    if (type == null)
                    {
                        builder.Free();
                        return null;
                    }

                    builder.Add(type.Value);

                    if (PeekNextChar() == ',')
                    {
                        ++_index;
                    }
                    else
                    {
                        break;
                    }
                }

                if (PeekNextChar() == '>')
                {
                    ++_index;
                }
                else
                {
                    builder.Free();
                    return null;
                }

                return builder.ToArrayAndFree();
            }
Example #23
0
        /// <summary>
        /// In the expanded form of a compound assignment (or increment/decrement), the LHS appears multiple times.
        /// If we aren't careful, this can result in repeated side-effects.  This creates (ordered) temps for all of the
        /// subexpressions that could result in side-effects and returns a side-effect-free expression that can be used
        /// in place of the LHS in the expanded form.
        /// </summary>
        /// <param name="originalLHS">The LHS sub-expression of the compound assignment (or increment/decrement).</param>
        /// <param name="stores">Populated with a list of assignment expressions that initialize the temporary locals.</param>
        /// <param name="temps">Populated with a list of temporary local symbols.</param>
        /// <param name="isDynamicAssignment">True if the compound assignment is a dynamic operation.</param>
        /// <returns>
        /// A side-effect-free expression representing the LHS.
        /// The returned node needs to be lowered but its children are already lowered.
        /// </returns>
        private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalLHS, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps, bool isDynamicAssignment)
        {
            // There are five possible cases.
            //
            // Case 1: receiver.Prop += value is transformed into
            // temp = receiver
            // temp.Prop = temp.Prop + value
            // and a later rewriting will turn that into calls to getters and setters.
            //
            // Case 2: collection[i1, i2, i3] += value is transformed into
            // tc = collection
            // t1 = i1
            // t2 = i2
            // t3 = i3
            // tc[t1, t2, t3] = tc[t1, t2, t3] + value
            // and again, a later rewriting will turn that into getters and setters of the indexer.
            //
            // Case 3: local += value (and param += value) needs no temporaries; it simply
            // becomes local = local + value.
            //
            // Case 4: staticField += value needs no temporaries either. However, classInst.field += value becomes
            // temp = classInst
            // temp.field = temp.field + value
            //
            // Case 5: otherwise, it must be structVariable.field += value or array[index] += value. Either way
            // we have a variable on the left. Transform it into:
            // ref temp = ref variable
            // temp = temp + value

            switch (originalLHS.Kind)
            {
            case BoundKind.PropertyAccess:
            {
                // We need to stash away the receiver so that it does not get evaluated twice.
                // If the receiver is classified as a value of reference type then we can simply say
                //
                // R temp = receiver
                // temp.prop = temp.prop + rhs
                //
                // But if the receiver is classified as a variable of struct type then we
                // cannot make a copy of the value; we need to make sure that we mutate
                // the original receiver, not the copy.  We have to generate
                //
                // ref R temp = ref receiver
                // temp.prop = temp.prop + rhs
                //
                // The rules of C# (in section 7.17.1) require that if you have receiver.prop
                // as the target of an assignment such that receiver is a value type, it must
                // be classified as a variable. If we've gotten this far in the rewriting,
                // assume that was the case.

                var prop = (BoundPropertyAccess)originalLHS;

                // If the property is static or is the receiver is of kind "Base" or "this", then we can just generate prop = prop + value
                if (prop.ReceiverOpt == null || prop.PropertySymbol.IsStatic || !NeedsTemp(prop.ReceiverOpt))
                {
                    return(prop);
                }

                Debug.Assert(prop.ReceiverOpt.Kind != BoundKind.TypeExpression);

                // Can we ever avoid storing the receiver in a temp? If the receiver is a variable then it
                // might be modified by the computation of the getter, the value, or the operation.
                // The receiver cannot be a null constant or constant of value type. It could be a
                // constant of string type, but there are no mutable properties of a string.
                // Similarly, there are no mutable properties of a Type object, so the receiver
                // cannot be a typeof(T) expression. The only situation we know of is where we could
                // optimize away the temp is if the receiver is a readonly field of reference type,
                // we are not in a constructor, and the receiver of the *field*, if any, is also idempotent.
                // It doesn't seem worthwhile to pursue an optimization for this exceedingly rare case.

                BoundExpression rewrittenReceiver = VisitExpression(prop.ReceiverOpt);
                if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType)
                {
                    var memberContainingType = prop.PropertySymbol.ContainingType;

                    // From the verifier prospective type parameters do not contain fields or methods.
                    // the instance must be boxed/constrained to access the member even if known to be a reference
                    // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice.
                    rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                }

                BoundAssignmentOperator assignmentToTemp;
                var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref);
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);

                // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties.
                return(new BoundPropertyAccess(prop.Syntax, receiverTemp, prop.PropertySymbol, prop.ResultKind, prop.Type));
            }

            case BoundKind.DynamicMemberAccess:
            {
                var memberAccess = (BoundDynamicMemberAccess)originalLHS;
                if (!NeedsTemp(memberAccess.Receiver))
                {
                    return(memberAccess);
                }

                // store receiver to temp:
                var rewrittenReceiver = VisitExpression(memberAccess.Receiver);
                BoundAssignmentOperator assignmentToTemp;
                var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);

                return(new BoundDynamicMemberAccess(memberAccess.Syntax, receiverTemp, memberAccess.TypeArgumentsOpt, memberAccess.Name, memberAccess.Invoked, memberAccess.Indexed, memberAccess.Type));
            }

            case BoundKind.IndexerAccess:
            {
                BoundIndexerAccess indexerAccess = (BoundIndexerAccess)originalLHS;

                var receiverOpt = indexerAccess.ReceiverOpt;
                Debug.Assert(receiverOpt != null);

                BoundExpression transformedReceiver;
                if (NeedsTemp(receiverOpt))
                {
                    BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);
                    if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType)
                    {
                        var memberContainingType = indexerAccess.Indexer.ContainingType;

                        // From the verifier prospective type parameters do not contain fields or methods.
                        // the instance must be boxed/constrained to access the member even if known to be a reference
                        // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice.
                        rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                    }

                    BoundAssignmentOperator assignmentToTemp;
                    var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref);
                    transformedReceiver = receiverTemp;
                    stores.Add(assignmentToTemp);
                    temps.Add(receiverTemp.LocalSymbol);
                }
                else
                {
                    transformedReceiver = VisitExpression(receiverOpt);
                }

                // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
                // we have to preserve both the source-code order of the side effects and the side effects
                // only being executed once.
                //
                // This is a subtly different problem than the problem faced by the conventional call
                // rewriter; with the conventional call rewriter we already know that the side effects
                // will only be executed once because the arguments are only being pushed on the stack once.
                // In a compound equality operator on an indexer the indices are placed on the stack twice.
                // That is to say, if you have:
                //
                // C().M(z : Z(), x : X(), y : Y())
                //
                // then we can rewrite that into
                //
                // tempc = C()
                // tempz = Z()
                // tempc.M(X(), Y(), tempz)
                //
                // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
                // temporaries in
                //
                // C().Collection[z : Z(), x : X(), y : Y()] += 1;
                //
                // because we have to ensure not just that Z() happens first, but in additioan that X() and Y() are only
                // called once.  We have to generate this as
                //
                // tempc = C().Collection
                // tempz = Z()
                // tempx = X()
                // tempy = Y()
                // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
                //
                // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
                // However, we can still do the optimization where constants are not stored in
                // temporaries; if we have
                //
                // C().Collection[z : 123, y : Y(), x : X()] += 1;
                //
                // Then we can generate that as
                //
                // tempc = C().Collection
                // tempx = X()
                // tempy = Y()
                // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;

                ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

                CSharpSyntaxNode         syntax           = indexerAccess.Syntax;
                PropertySymbol           indexer          = indexerAccess.Indexer;
                ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
                bool expanded = indexerAccess.Expanded;
                ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

                ImmutableArray <ParameterSymbol> parameters          = indexer.Parameters;
                BoundExpression[] actualArguments                    = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
                ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

                ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

                // Step one: Store everything that is non-trivial into a temporary; record the
                // stores in storesToTemps and make the actual argument a reference to the temp.
                // Do not yet attempt to deal with params arrays or optional arguments.
                BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps);

                // Step two: If we have a params array, build the array and fill in the argument.
                if (expanded)
                {
                    BoundExpression         array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                    BoundAssignmentOperator storeToTemp;
                    var boundTemp = this.factory.StoreToTemp(array, out storeToTemp);
                    stores.Add(storeToTemp);
                    temps.Add(boundTemp.LocalSymbol);
                    actualArguments[actualArguments.Length - 1] = boundTemp;
                }

                // Step three: Now fill in the optional arguments. (Dev11 uses the
                // getter for optional arguments in compound assignments.)
                var getMethod = indexer.GetOwnOrInheritedGetMethod();
                Debug.Assert((object)getMethod != null);
                InsertMissingOptionalArguments(syntax, getMethod.Parameters, actualArguments);

                // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
                // duplicate side-effects, so we'll skip that step.

                if (indexer.ContainingType.IsComImport)
                {
                    RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
                }

                rewrittenArguments = actualArguments.AsImmutableOrNull();

                foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
                {
                    temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                    stores.Add(tempAssignment);
                }

                storesToTemps.Free();
                argumentRefKinds = GetRefKindsOrNull(refKinds);
                refKinds.Free();

                // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
                return(new BoundIndexerAccess(
                           syntax,
                           transformedReceiver,
                           indexer,
                           rewrittenArguments,
                           default(ImmutableArray <string>),
                           argumentRefKinds,
                           false,
                           default(ImmutableArray <int>),
                           indexerAccess.Type));
            }

            case BoundKind.Local:
            case BoundKind.Parameter:
            case BoundKind.ThisReference:     // a special kind of parameter
                // No temporaries are needed. Just generate local = local + value
                return(originalLHS);

            case BoundKind.DeclarationExpression:
                var rewrittenDeclExpr = (BoundExpression)VisitDeclarationExpression((BoundDeclarationExpression)originalLHS);

                switch (rewrittenDeclExpr.Kind)
                {
                case BoundKind.Local:
                    return(rewrittenDeclExpr);

                case BoundKind.Sequence:
                    var sequence = (BoundSequence)rewrittenDeclExpr;
                    stores.AddRange(sequence.SideEffects);
                    temps.AddRange(sequence.Locals);

                    if (sequence.Value.Kind == BoundKind.Local)
                    {
                        return(sequence.Value);
                    }

                    break;
                }

                throw ExceptionUtilities.Unreachable;

            case BoundKind.FieldAccess:
            {
                // * If the field is static then no temporaries are needed.
                // * If the field is not static and the receiver is of reference type then generate t = r; t.f = t.f + value
                // * If the field is not static and the receiver is a variable of value type then we'll fall into the
                //   general variable case below.

                var             fieldAccess = (BoundFieldAccess)originalLHS;
                BoundExpression receiverOpt = fieldAccess.ReceiverOpt;

                //If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
                if (receiverOpt == null || fieldAccess.FieldSymbol.IsStatic || !NeedsTemp(receiverOpt))
                {
                    return(fieldAccess);
                }

                if (receiverOpt.Type.IsReferenceType)
                {
                    Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression);
                    BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                    if (rewrittenReceiver.Type.IsTypeParameter())
                    {
                        var memberContainingType = fieldAccess.FieldSymbol.ContainingType;

                        // From the verifier prospective type parameters do not contain fields or methods.
                        // the instance must be "boxed" to access the field
                        // It makes sense to box receiver before storing into a temp - no need to box twice.
                        rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                    }

                    BoundAssignmentOperator assignmentToTemp;
                    var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                    stores.Add(assignmentToTemp);
                    temps.Add(receiverTemp.LocalSymbol);
                    return(new BoundFieldAccess(fieldAccess.Syntax, receiverTemp, fieldAccess.FieldSymbol, null));
                }

                break;
            }

            case BoundKind.DynamicIndexerAccess:
            {
                var indexerAccess = (BoundDynamicIndexerAccess)originalLHS;

                BoundExpression loweredReceiver;
                if (NeedsTemp(indexerAccess.ReceiverOpt))
                {
                    BoundAssignmentOperator assignmentToTemp;
                    var temp = this.factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp);
                    stores.Add(assignmentToTemp);
                    temps.Add(temp.LocalSymbol);
                    loweredReceiver = temp;
                }
                else
                {
                    loweredReceiver = indexerAccess.ReceiverOpt;
                }

                var arguments        = indexerAccess.Arguments;
                var loweredArguments = new BoundExpression[arguments.Length];

                for (int i = 0; i < arguments.Length; i++)
                {
                    if (NeedsTemp(arguments[i]))
                    {
                        BoundAssignmentOperator assignmentToTemp;
                        var temp = this.factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, refKind: indexerAccess.ArgumentRefKindsOpt.RefKinds(i));
                        stores.Add(assignmentToTemp);
                        temps.Add(temp.LocalSymbol);
                        loweredArguments[i] = temp;
                    }
                    else
                    {
                        loweredArguments[i] = arguments[i];
                    }
                }

                return(new BoundDynamicIndexerAccess(
                           indexerAccess.Syntax,
                           loweredReceiver,
                           loweredArguments.AsImmutableOrNull(),
                           indexerAccess.ArgumentNamesOpt,
                           indexerAccess.ArgumentRefKindsOpt,
                           indexerAccess.ApplicableIndexers,
                           indexerAccess.Type));
            }

            case BoundKind.ArrayAccess:
                if (isDynamicAssignment)
                {
                    // In non-dynamic array[index] op= R we emit:
                    //   T& tmp = &array[index];
                    //   *tmp = *L op R;
                    // where T is the type of L.
                    //
                    // If L is an array access, the assignment is dynamic, the compile-time of the array is dynamic[]
                    // and the runtime type of the array is not object[] (but e.g. string[]) the pointer approach is broken.
                    // T is Object in such case and we can't take a read-write pointer of type Object& to an array element of non-object type.
                    //
                    // In this case we rewrite the assignment as follows:
                    //
                    //   E t_array = array;
                    //   I t_index = index; (possibly more indices)
                    //   T value = t_array[t_index];
                    //   t_array[t_index] = value op R;

                    var arrayAccess    = (BoundArrayAccess)originalLHS;
                    var loweredArray   = VisitExpression(arrayAccess.Expression);
                    var loweredIndices = VisitList(arrayAccess.Indices);

                    return(SpillArrayElementAccess(loweredArray, loweredIndices, stores, temps));
                }

                break;

            case BoundKind.PointerElementAccess:
            case BoundKind.PointerIndirectionOperator:
            case BoundKind.RefValueOperator:
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(originalLHS.Kind);
            }

            // We made no transformation above. Either we have array[index] += value or
            // structVariable.field += value; either way we have a potentially complicated variable-
            // producing expression on the left. Generate
            // ref temp = ref variable; temp = temp + value

            // Rewrite the variable.  Here we depend on the fact that the only forms
            // rewritten here are rewritten the same for lvalues and rvalues.
            BoundExpression rewrittenVariable = VisitExpression(originalLHS);

            BoundAssignmentOperator assignmentToTemp2;
            var variableTemp = this.factory.StoreToTemp(rewrittenVariable, out assignmentToTemp2, refKind: RefKind.Ref);

            stores.Add(assignmentToTemp2);
            temps.Add(variableTemp.LocalSymbol);
            return(variableTemp);
        }
Example #24
0
        private IEnumerable<CSharpAttributeData> GetCustomAttributesToEmitIterator(
            ImmutableArray<CSharpAttributeData> userDefined,
            ArrayBuilder<SynthesizedAttributeData> synthesized,
            bool isReturnType,
            bool emittingAssemblyAttributesInNetModule)
        {
            CheckDefinitionInvariant();

            if (synthesized != null)
            {
                foreach (var attribute in synthesized)
                {
                    // only synthesize attributes that are emitted:
                    Debug.Assert(attribute.ShouldEmitAttribute(this, isReturnType, emittingAssemblyAttributesInNetModule));
                    yield return attribute;
                }

                synthesized.Free();
            }

            for (int i = 0; i < userDefined.Length; i++)
            {
                CSharpAttributeData attribute = userDefined[i];
                if (this.Kind == SymbolKind.Assembly)
                {
                    // We need to filter out duplicate assembly attributes (i.e. attributes that
                    // bind to the same constructor and have identical arguments) and invalid
                    // InternalsVisibleTo attributes.
                    if (((SourceAssemblySymbol)this).IsIndexOfOmittedAssemblyAttribute(i))
                    {
                        continue;
                    }
                }

                if (attribute.ShouldEmitAttribute(this, isReturnType, emittingAssemblyAttributesInNetModule))
                {
                    yield return attribute;
                }
            }
        }
Example #25
0
 public void Free()
 {
     _temps.Free();
     _map.Free();
 }
 public void Free()
 {
     Operations?.Free();
 }
Example #27
0
 internal void Free()
 {
     init.Free();
 }
Example #28
0
 public void FreePooledObjects()
 {
     stack0.Free();
     stack1.Free();
     discriminatorStack.Free();
 }
Example #29
0
        internal static SourceText ToSourceTextAndFree(ArrayBuilder<SourceText> segments, SourceText original, bool adjustSegments)
        {
            if (adjustSegments)
            {
                TrimInaccessibleText(segments);
                ReduceSegmentCountIfNecessary(segments);
            }

            if (segments.Count == 0)
            {
                segments.Free();
                return SourceText.From(string.Empty, original.Encoding, original.ChecksumAlgorithm);
            }
            else if (segments.Count == 1)
            {
                SourceText result = segments[0];
                segments.Free();
                return result;
            }
            else
            {
                return new CompositeText(segments.ToImmutableAndFree(), original.Encoding, original.ChecksumAlgorithm);
            }
        }
Example #30
0
        internal static string GetSerializedTypeName(this ITypeReference typeReference, EmitContext context, ref bool isAssemblyQualified)
        {
            var                 pooled  = PooledStringBuilder.GetInstance();
            StringBuilder       sb      = pooled.Builder;
            IArrayTypeReference arrType = typeReference as IArrayTypeReference;

            if (arrType != null)
            {
                typeReference = arrType.GetElementType(context);
                bool isAssemQual = false;
                AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context);
                if (arrType.IsSZArray)
                {
                    sb.Append("[]");
                }
                else
                {
                    sb.Append('[');
                    if (arrType.Rank == 1)
                    {
                        sb.Append('*');
                    }

                    sb.Append(',', (int)arrType.Rank - 1);

                    sb.Append(']');
                }

                goto done;
            }

            IPointerTypeReference pointer = typeReference as IPointerTypeReference;

            if (pointer != null)
            {
                typeReference = pointer.GetTargetType(context);
                bool isAssemQual = false;
                AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context);
                sb.Append('*');
                goto done;
            }

            IManagedPointerTypeReference reference = typeReference as IManagedPointerTypeReference;

            if (reference != null)
            {
                typeReference = reference.GetTargetType(context);
                bool isAssemQual = false;
                AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context);
                sb.Append('&');
                goto done;
            }

            INamespaceTypeReference namespaceType = typeReference.AsNamespaceTypeReference;

            if (namespaceType != null)
            {
                var name = namespaceType.NamespaceName;
                if (name.Length != 0)
                {
                    sb.Append(name);
                    sb.Append('.');
                }

                sb.Append(GetMangledAndEscapedName(namespaceType));
                goto done;
            }

            if (typeReference.IsTypeSpecification())
            {
                ITypeReference uninstantiatedTypeReference = typeReference.GetUninstantiatedGenericType();

                ArrayBuilder <ITypeReference> consolidatedTypeArguments = ArrayBuilder <ITypeReference> .GetInstance();

                typeReference.GetConsolidatedTypeArguments(consolidatedTypeArguments, context);

                bool uninstantiatedTypeIsAssemblyQualified = false;
                sb.Append(GetSerializedTypeName(uninstantiatedTypeReference, context, ref uninstantiatedTypeIsAssemblyQualified));
                sb.Append('[');
                bool first = true;
                foreach (ITypeReference argument in consolidatedTypeArguments)
                {
                    if (first)
                    {
                        first = false;
                    }
                    else
                    {
                        sb.Append(',');
                    }

                    bool isAssemQual = true;
                    AppendSerializedTypeName(sb, argument, ref isAssemQual, context);
                }
                consolidatedTypeArguments.Free();

                sb.Append(']');
                goto done;
            }

            INestedTypeReference nestedType = typeReference.AsNestedTypeReference;

            if (nestedType != null)
            {
                bool nestedTypeIsAssemblyQualified = false;
                sb.Append(GetSerializedTypeName(nestedType.GetContainingType(context), context, ref nestedTypeIsAssemblyQualified));
                sb.Append('+');
                sb.Append(GetMangledAndEscapedName(nestedType));
                goto done;
            }

            // TODO: error
done:
            if (isAssemblyQualified)
            {
                AppendAssemblyQualifierIfNecessary(sb, UnwrapTypeReference(typeReference, context), out isAssemblyQualified, context);
            }

            return(pooled.ToStringAndFree());
        }
Example #31
0
        private PatternMatch?NonFuzzyMatchPatternChunk(
            string candidate,
            TextChunk patternChunk,
            bool punctuationStripped)
        {
            var candidateLength = candidate.Length;

            var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase);

            if (caseInsensitiveIndex == 0)
            {
                // We found the pattern at the start of the candidate.  This is either an exact or
                // prefix match.

                if (patternChunk.Text.Length == candidateLength)
                {
                    // Lengths were the same, this is either a case insensitive or sensitive exact match.
                    return(new PatternMatch(
                               PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: candidate == patternChunk.Text,
                               matchedSpan: GetMatchedSpan(0, candidateLength)));
                }
                else
                {
                    // Lengths were the same, this is either a case insensitive or sensitive prefix match.
                    return(new PatternMatch(
                               PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text),
                               matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)));
                }
            }

            ArrayBuilder <TextSpan> candidateHumpsOpt = null;

            try
            {
                var patternIsLowercase = patternChunk.IsLowercase;
                if (caseInsensitiveIndex > 0)
                {
                    // We found the pattern somewhere in the candidate.  This could be a substring match.
                    // However, we don't want to be overaggressive in returning just any substring results.
                    // So do a few more checks to make sure this is a good result.

                    if (!patternIsLowercase)
                    {
                        // Pattern contained uppercase letters.  This is a strong indication from the
                        // user that they expect the same letters to be uppercase in the result.  As
                        // such, only return this if we can find this pattern exactly in the candidate.

                        var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None);
                        if (caseSensitiveIndex > 0)
                        {
                            if (char.IsUpper(candidate[caseInsensitiveIndex]))
                            {
                                return(new PatternMatch(
                                           PatternMatchKind.StartOfWordSubstring, punctuationStripped, isCaseSensitive: true,
                                           matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length)));
                            }
                            else
                            {
                                return(new PatternMatch(
                                           PatternMatchKind.NonLowercaseSubstring, punctuationStripped, isCaseSensitive: true,
                                           matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length)));
                            }
                        }
                    }
                    else
                    {
                        // Pattern was all lowercase.  This can lead to lots of hits.  For example, "bin" in
                        // "CombineUnits".  Instead, we want it to match "Operator[|Bin|]ary" first rather than
                        // Com[|bin|]eUnits

                        // If the lowercase search string matched what looks to be the start of a word then that's a
                        // reasonable hit. This is equivalent to 'bin' matching 'Operator[|Bin|]ary'
                        if (char.IsUpper(candidate[caseInsensitiveIndex]))
                        {
                            return(new PatternMatch(PatternMatchKind.StartOfWordSubstring, punctuationStripped,
                                                    isCaseSensitive: false,
                                                    matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length)));
                        }

                        // Now do the more expensive check to see if we're at the start of a word.  This is to catch
                        // word matches like CombineBinary.  We want to find the hit against '[|Bin|]ary' not
                        // 'Com[|bin|]e'
                        candidateHumpsOpt = StringBreaker.GetWordParts(candidate);
                        for (int i = 0, n = candidateHumpsOpt.Count; i < n; i++)
                        {
                            var hump = TextSpan.FromBounds(candidateHumpsOpt[i].Start, candidateLength);
                            if (PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.IgnoreCase))
                            {
                                return(new PatternMatch(PatternMatchKind.StartOfWordSubstring, punctuationStripped,
                                                        isCaseSensitive: PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.None),
                                                        matchedSpan: GetMatchedSpan(hump.Start, patternChunk.Text.Length)));
                            }
                        }
                    }
                }

                // Didn't have an exact/prefix match, or a high enough quality substring match.
                // See if we can find a camel case match.
                if (candidateHumpsOpt == null)
                {
                    candidateHumpsOpt = StringBreaker.GetWordParts(candidate);
                }

                // Didn't have an exact/prefix match, or a high enough quality substring match.
                // See if we can find a camel case match.
                var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumpsOpt);
                if (match != null)
                {
                    return(match);
                }

                // If pattern was all lowercase, we allow it to match an all lowercase section of the candidate.  But
                // only after we've tried all other forms first.  This is the weakest of all matches.  For example, if
                // user types 'bin' we want to match 'OperatorBinary' (start of word) or 'BinaryInformationNode' (camel
                // humps) before matching 'Combine'.
                //
                // We only do this for strings longer than three characters to avoid too many false positives when the
                // user has only barely started writing a word.
                if (patternIsLowercase && caseInsensitiveIndex > 0 && patternChunk.Text.Length >= 3)
                {
                    var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None);
                    if (caseSensitiveIndex > 0)
                    {
                        return(new PatternMatch(
                                   PatternMatchKind.LowercaseSubstring, punctuationStripped, isCaseSensitive: true,
                                   matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length)));
                    }
                }

                return(null);
            }
            finally
            {
                candidateHumpsOpt?.Free();
            }
        }
            internal void Free()
            {
                ArrayBuilder <V> arrayBuilder = _value as ArrayBuilder <V>;

                arrayBuilder?.Free();
            }
Example #33
0
        private void GetFrameName(
            DkmInspectionContext inspectionContext,
            DkmWorkList workList,
            DkmStackWalkFrame frame,
            DkmVariableInfoFlags argumentFlags,
            DkmCompletionRoutine <DkmGetFrameNameAsyncResult> completionRoutine,
            TMethodSymbol method)
        {
            var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types);
            var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names);

            if (argumentFlags.Includes(DkmVariableInfoFlags.Values))
            {
                // No need to compute the Expandable bit on
                // argument values since that can be expensive.
                inspectionContext = DkmInspectionContext.Create(
                    inspectionContext.InspectionSession,
                    inspectionContext.RuntimeInstance,
                    inspectionContext.Thread,
                    inspectionContext.Timeout,
                    inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion,
                    inspectionContext.FuncEvalFlags,
                    inspectionContext.Radix,
                    inspectionContext.Language,
                    inspectionContext.ReturnValue,
                    inspectionContext.AdditionalVisualizationData,
                    inspectionContext.AdditionalVisualizationDataPriority,
                    inspectionContext.ReturnValues);

                // GetFrameArguments returns an array of formatted argument values. We'll pass
                // ourselves (GetFrameName) as the continuation of the GetFrameArguments call.
                inspectionContext.GetFrameArguments(
                    workList,
                    frame,
                    result =>
                {
                    // DkmGetFrameArgumentsAsyncResult.Arguments will throw if ErrorCode != 0.
                    var argumentValues = (result.ErrorCode == 0) ? result.Arguments : null;
                    try
                    {
                        ArrayBuilder <string> builder = null;
                        if (argumentValues != null)
                        {
                            builder = ArrayBuilder <string> .GetInstance();
                            foreach (var argument in argumentValues)
                            {
                                var formattedArgument = argument as DkmSuccessEvaluationResult;
                                // Not expecting Expandable bit, at least not from this EE.
                                Debug.Assert((formattedArgument == null) || (formattedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0);
                                builder.Add(formattedArgument?.Value);
                            }
                        }

                        var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, argumentValues: builder);
                        builder?.Free();
                        completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
                    }
                    catch (Exception e)
                    {
                        completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e));
                    }
                    finally
                    {
                        if (argumentValues != null)
                        {
                            foreach (var argument in argumentValues)
                            {
                                argument.Close();
                            }
                        }
                    }
                });
            }
            else
            {
                var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, argumentValues: null);
                completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
            }
        }
        private PatternMatch?NonFuzzyMatchPatternChunk(
            string candidate,
            TextChunk patternChunk,
            bool punctuationStripped)
        {
            var candidateLength = candidate.Length;

            var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase);

            if (caseInsensitiveIndex == 0)
            {
                // We found the pattern at the start of the candidate.  This is either an exact or
                // prefix match.

                if (patternChunk.Text.Length == candidateLength)
                {
                    // Lengths were the same, this is either a case insensitive or sensitive exact match.
                    return(new PatternMatch(
                               PatternMatchKind.Exact, punctuationStripped, isCaseSensitive: candidate == patternChunk.Text,
                               matchedSpan: GetMatchedSpan(0, candidateLength)));
                }
                else
                {
                    // Lengths were the same, this is either a case insensitive or sensitive prefix match.
                    return(new PatternMatch(
                               PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text),
                               matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)));
                }
            }

            ArrayBuilder <TextSpan> candidateHumpsOpt = null;

            try
            {
                var patternIsLowercase = patternChunk.IsLowercase;
                if (caseInsensitiveIndex > 0)
                {
                    // We found the pattern somewhere in the candidate.  This could be a substring match.
                    // However, we don't want to be overaggressive in returning just any substring results.
                    // So do a few more checks to make sure this is a good result.

                    if (!patternIsLowercase)
                    {
                        // Pattern contained uppercase letters.  This is a strong indication from the
                        // user that they expect the same letters to be uppercase in the result.  As
                        // such, only return this if we can find this pattern exactly in the candidate.

                        var caseSensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.None);
                        if (caseSensitiveIndex > 0)
                        {
                            return(new PatternMatch(
                                       PatternMatchKind.Substring, punctuationStripped, isCaseSensitive: true,
                                       matchedSpan: GetMatchedSpan(caseSensitiveIndex, patternChunk.Text.Length)));
                        }
                    }
                    else
                    {
                        // Pattern was all lowercase.  This can lead to lots of false positives.  For
                        // example, we don't want "bin" to match "CombineUnits".  Instead, we want it
                        // to match "BinaryOperator".  As such, make sure our match looks like it's
                        // starting an actual word in the candidate.

                        // Do a quick check to avoid the expensive work of having to go get the candidate
                        // humps.
                        if (char.IsUpper(candidate[caseInsensitiveIndex]))
                        {
                            return(new PatternMatch(PatternMatchKind.Substring, punctuationStripped,
                                                    isCaseSensitive: false,
                                                    matchedSpan: GetMatchedSpan(caseInsensitiveIndex, patternChunk.Text.Length)));
                        }

                        candidateHumpsOpt = StringBreaker.GetWordParts(candidate);
                        for (int i = 0, n = candidateHumpsOpt.Count; i < n; i++)
                        {
                            var hump = TextSpan.FromBounds(candidateHumpsOpt[i].Start, candidateLength);
                            if (PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.IgnoreCase))
                            {
                                return(new PatternMatch(PatternMatchKind.Substring, punctuationStripped,
                                                        isCaseSensitive: PartStartsWith(candidate, hump, patternChunk.Text, CompareOptions.None),
                                                        matchedSpan: GetMatchedSpan(hump.Start, patternChunk.Text.Length)));
                            }
                        }
                    }
                }

                // Didn't have an exact/prefix match, or a high enough quality substring match.
                // See if we can find a camel case match.
                if (candidateHumpsOpt == null)
                {
                    candidateHumpsOpt = StringBreaker.GetWordParts(candidate);
                }

                // Didn't have an exact/prefix match, or a high enough quality substring match.
                // See if we can find a camel case match.
                return(TryCamelCaseMatch(
                           candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumpsOpt));
            }
            finally
            {
                candidateHumpsOpt?.Free();
            }
        }
Example #35
0
        /// <summary>
        /// At this point, we have a list of viable symbols and no parameter list with which to perform
        /// overload resolution.  We'll just return the first symbol, giving a diagnostic if there are
        /// others.
        /// Caveat: If there are multiple candidates and only one is from source, then the source symbol
        /// wins and no diagnostic is reported.
        /// </summary>
        private ImmutableArray <Symbol> ProcessParameterlessCrefMemberLookupResults(
            ImmutableArray <Symbol> symbols,
            int arity,
            MemberCrefSyntax memberSyntax,
            TypeArgumentListSyntax typeArgumentListSyntax,
            out Symbol ambiguityWinner,
            DiagnosticBag diagnostics)
        {
            // If the syntax indicates arity zero, then we match methods of any arity.
            // However, if there are both generic and non-generic methods, then the
            // generic methods should be ignored.
            if (symbols.Length > 1 && arity == 0)
            {
                bool hasNonGenericMethod = false;
                bool hasGenericMethod    = false;
                foreach (Symbol s in symbols)
                {
                    if (s.Kind != SymbolKind.Method)
                    {
                        continue;
                    }

                    if (((MethodSymbol)s).Arity == 0)
                    {
                        hasNonGenericMethod = true;
                    }
                    else
                    {
                        hasGenericMethod = true;
                    }

                    if (hasGenericMethod && hasNonGenericMethod)
                    {
                        break; //Nothing else to be learned.
                    }
                }

                if (hasNonGenericMethod && hasGenericMethod)
                {
                    symbols = symbols.WhereAsArray(s =>
                                                   s.Kind != SymbolKind.Method || ((MethodSymbol)s).Arity == 0);
                }
            }

            Debug.Assert(!symbols.IsEmpty);

            Symbol symbol = symbols[0];

            // If there's ambiguity, prefer source symbols.
            // Logic is similar to ResultSymbol, but separate because the error handling is totally different.
            if (symbols.Length > 1)
            {
                // Size is known, but IndexOfSymbolFromCurrentCompilation expects a builder.
                ArrayBuilder <Symbol> unwrappedSymbols = ArrayBuilder <Symbol> .GetInstance(symbols.Length);

                foreach (Symbol wrapped in symbols)
                {
                    unwrappedSymbols.Add(UnwrapAliasNoDiagnostics(wrapped));
                }

                BestSymbolInfo secondBest;
                BestSymbolInfo best = GetBestSymbolInfo(unwrappedSymbols, out secondBest);

                Debug.Assert(!best.IsNone);
                Debug.Assert(!secondBest.IsNone);

                unwrappedSymbols.Free();

                int symbolIndex = 0;

                if (best.IsFromCompilation)
                {
                    symbolIndex = best.Index;
                    symbol      = symbols[symbolIndex]; // NOTE: symbols, not unwrappedSymbols.
                }

                if (symbol.Kind == SymbolKind.TypeParameter)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
                }
                else if (secondBest.IsFromCompilation == best.IsFromCompilation)
                {
                    CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                    int        otherIndex = symbolIndex == 0 ? 1 : 0;
                    diagnostics.Add(ErrorCode.WRN_AmbiguousXMLReference, crefSyntax.Location, crefSyntax.ToString(), symbol, symbols[otherIndex]);

                    ambiguityWinner = ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol);
                    return(symbols.SelectAsArray(sym => ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, sym)));
                }
            }
            else if (symbol.Kind == SymbolKind.TypeParameter)
            {
                CrefSyntax crefSyntax = GetRootCrefSyntax(memberSyntax);
                diagnostics.Add(ErrorCode.WRN_BadXMLRefTypeVar, crefSyntax.Location, crefSyntax.ToString());
            }

            ambiguityWinner = null;
            return(ImmutableArray.Create <Symbol>(ConstructWithCrefTypeParameters(arity, typeArgumentListSyntax, symbol)));
        }