Beispiel #1
0
        /// <summary>
        /// Create a context to compile expressions within a method scope.
        /// </summary>
        internal CompilationContext(
            CSharpCompilation compilation,
            MethodSymbol currentFrame,
            ImmutableArray<LocalSymbol> locals,
            InScopeHoistedLocals inScopeHoistedLocals,
            MethodDebugInfo<TypeSymbol, LocalSymbol> methodDebugInfo,
            CSharpSyntaxNode syntax)
        {
            Debug.Assert((syntax == null) || (syntax is ExpressionSyntax) || (syntax is LocalDeclarationStatementSyntax));

            // TODO: syntax.SyntaxTree should probably be added to the compilation,
            // but it isn't rooted by a CompilationUnitSyntax so it doesn't work (yet).
            _currentFrame = currentFrame;
            _syntax = syntax;
            _methodNotType = !locals.IsDefault;

            // NOTE: Since this is done within CompilationContext, it will not be cached.
            // CONSIDER: The values should be the same everywhere in the module, so they
            // could be cached.  
            // (Catch: what happens in a type context without a method def?)
            this.Compilation = GetCompilationWithExternAliases(compilation, methodDebugInfo.ExternAliasRecords);

            // Each expression compile should use a unique compilation
            // to ensure expression-specific synthesized members can be
            // added (anonymous types, for instance).
            Debug.Assert(this.Compilation != compilation);

            this.NamespaceBinder = CreateBinderChain(
                this.Compilation,
                (PEModuleSymbol)currentFrame.ContainingModule,
                currentFrame.ContainingNamespace,
                methodDebugInfo.ImportRecordGroups);

            if (_methodNotType)
            {
                _locals = locals;
                ImmutableArray<string> displayClassVariableNamesInOrder;
                GetDisplayClassVariables(
                    currentFrame,
                    _locals,
                    inScopeHoistedLocals,
                    out displayClassVariableNamesInOrder,
                    out _displayClassVariables,
                    out _hoistedParameterNames);
                Debug.Assert(displayClassVariableNamesInOrder.Length == _displayClassVariables.Count);
                _localsForBinding = GetLocalsForBinding(_locals, displayClassVariableNamesInOrder, _displayClassVariables);
            }
            else
            {
                _locals = ImmutableArray<LocalSymbol>.Empty;
                _displayClassVariables = ImmutableDictionary<string, DisplayClassVariable>.Empty;
                _localsForBinding = ImmutableArray<LocalSymbol>.Empty;
            }

            // Assert that the cheap check for "this" is equivalent to the expensive check for "this".
            Debug.Assert(
                _displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName()) ==
                _displayClassVariables.Values.Any(v => v.Kind == DisplayClassVariableKind.This));
        }
Beispiel #2
0
        private EvaluationContext(
            MethodContextReuseConstraints? methodContextReuseConstraints,
            CSharpCompilation compilation,
            MethodSymbol currentFrame,
            ImmutableArray<LocalSymbol> locals,
            InScopeHoistedLocals inScopeHoistedLocals,
            MethodDebugInfo<TypeSymbol, LocalSymbol> methodDebugInfo)
        {
            Debug.Assert(inScopeHoistedLocals != null);
            Debug.Assert(methodDebugInfo != null);

            this.MethodContextReuseConstraints = methodContextReuseConstraints;
            this.Compilation = compilation;
            _currentFrame = currentFrame;
            _locals = locals;
            _inScopeHoistedLocals = inScopeHoistedLocals;
            _methodDebugInfo = methodDebugInfo;
        }
Beispiel #3
0
        private static void GetDisplayClassVariables(
            ArrayBuilder<string> displayClassVariableNamesInOrderBuilder,
            Dictionary<string, DisplayClassVariable> displayClassVariablesBuilder,
            HashSet<string> parameterNames,
            InScopeHoistedLocals inScopeHoistedLocals,
            DisplayClassInstanceAndFields instance,
            HashSet<string> hoistedParameterNames)
        {
            // Display class instance. The display class fields are variables.
            foreach (var member in instance.Type.GetMembers())
            {
                if (member.Kind != SymbolKind.Field)
                {
                    continue;
                }

                var field = (FieldSymbol)member;
                var fieldName = field.Name;

            REPARSE:

                DisplayClassVariableKind variableKind;
                string variableName;
                GeneratedNameKind fieldKind;
                int openBracketOffset;
                int closeBracketOffset;
                GeneratedNames.TryParseGeneratedName(fieldName, out fieldKind, out openBracketOffset, out closeBracketOffset);

                switch (fieldKind)
                {
                    case GeneratedNameKind.AnonymousTypeField:
                        Debug.Assert(fieldName == field.Name); // This only happens once.
                        fieldName = fieldName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
                        goto REPARSE;
                    case GeneratedNameKind.TransparentIdentifier:
                        // A transparent identifier (field) in an anonymous type synthesized for a transparent identifier.
                        Debug.Assert(!field.IsStatic);
                        continue;
                    case GeneratedNameKind.DisplayClassLocalOrField:
                        // A local that is itself a display class instance.
                        Debug.Assert(!field.IsStatic);
                        continue;
                    case GeneratedNameKind.HoistedLocalField:
                        // Filter out hoisted locals that are known to be out-of-scope at the current IL offset.
                        // Hoisted locals with invalid indices will be included since more information is better
                        // than less in error scenarios.
                        if (!inScopeHoistedLocals.IsInScope(fieldName))
                        {
                            continue;
                        }

                        variableName = fieldName.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
                        variableKind = DisplayClassVariableKind.Local;
                        Debug.Assert(!field.IsStatic);
                        break;
                    case GeneratedNameKind.ThisProxyField:
                        // A reference to "this".
                        variableName = fieldName;
                        variableKind = DisplayClassVariableKind.This;
                        Debug.Assert(!field.IsStatic);
                        break;
                    case GeneratedNameKind.None:
                        // A reference to a parameter or local.
                        variableName = fieldName;
                        if (parameterNames.Contains(variableName))
                        {
                            variableKind = DisplayClassVariableKind.Parameter;
                            hoistedParameterNames.Add(variableName);
                        }
                        else
                        {
                            variableKind = DisplayClassVariableKind.Local;
                        }
                        Debug.Assert(!field.IsStatic);
                        break;
                    default:
                        continue;
                }

                if (displayClassVariablesBuilder.ContainsKey(variableName))
                {
                    // Only expecting duplicates for async state machine
                    // fields (that should be at the top-level).
                    Debug.Assert(displayClassVariablesBuilder[variableName].DisplayClassFields.Count() == 1);
                    Debug.Assert(instance.Fields.Count() >= 1); // greater depth
                    Debug.Assert((variableKind == DisplayClassVariableKind.Parameter) ||
                        (variableKind == DisplayClassVariableKind.This));
                }
                else if (variableKind != DisplayClassVariableKind.This || GeneratedNames.GetKind(instance.Type.ContainingType.Name) != GeneratedNameKind.LambdaDisplayClass)
                {
                    // In async lambdas, the hoisted "this" field in the state machine type will point to the display class instance, if there is one.
                    // In such cases, we want to add the display class "this" to the map instead (or nothing, if it lacks one).
                    displayClassVariableNamesInOrderBuilder.Add(variableName);
                    displayClassVariablesBuilder.Add(variableName, instance.ToVariable(variableName, variableKind, field));
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Return a mapping of captured variables (parameters, locals, and
        /// "this") to locals. The mapping is needed to expose the original
        /// local identifiers (those from source) in the binder.
        /// </summary>
        private static void GetDisplayClassVariables(
            MethodSymbol method,
            ImmutableArray<LocalSymbol> locals,
            InScopeHoistedLocals inScopeHoistedLocals,
            out ImmutableArray<string> displayClassVariableNamesInOrder,
            out ImmutableDictionary<string, DisplayClassVariable> displayClassVariables,
            out ImmutableHashSet<string> hoistedParameterNames)
        {
            // Calculated the shortest paths from locals to instances of display
            // classes. There should not be two instances of the same display
            // class immediately within any particular method.
            var displayClassTypes = PooledHashSet<NamedTypeSymbol>.GetInstance();
            var displayClassInstances = ArrayBuilder<DisplayClassInstanceAndFields>.GetInstance();

            // Add any display class instances from locals (these will contain any hoisted locals).
            foreach (var local in locals)
            {
                var name = local.Name;
                if ((name != null) && (GeneratedNames.GetKind(name) == GeneratedNameKind.DisplayClassLocalOrField))
                {
                    var instance = new DisplayClassInstanceFromLocal((EELocalSymbol)local);
                    displayClassTypes.Add(instance.Type);
                    displayClassInstances.Add(new DisplayClassInstanceAndFields(instance));
                }
            }

            foreach (var parameter in method.Parameters)
            {
                if (GeneratedNames.GetKind(parameter.Name) == GeneratedNameKind.TransparentIdentifier)
                {
                    var instance = new DisplayClassInstanceFromParameter(parameter);
                    displayClassTypes.Add(instance.Type);
                    displayClassInstances.Add(new DisplayClassInstanceAndFields(instance));
                }
            }

            var containingType = method.ContainingType;
            bool isIteratorOrAsyncMethod = false;
            if (IsDisplayClassType(containingType))
            {
                if (!method.IsStatic)
                {
                    // Add "this" display class instance.
                    var instance = new DisplayClassInstanceFromParameter(method.ThisParameter);
                    displayClassTypes.Add(instance.Type);
                    displayClassInstances.Add(new DisplayClassInstanceAndFields(instance));
                }

                isIteratorOrAsyncMethod = GeneratedNames.GetKind(containingType.Name) == GeneratedNameKind.StateMachineType;
            }

            if (displayClassInstances.Any())
            {
                // Find any additional display class instances breadth first.
                for (int depth = 0; GetDisplayClassInstances(displayClassTypes, displayClassInstances, depth) > 0; depth++)
                {
                }

                // The locals are the set of all fields from the display classes.
                var displayClassVariableNamesInOrderBuilder = ArrayBuilder<string>.GetInstance();
                var displayClassVariablesBuilder = PooledDictionary<string, DisplayClassVariable>.GetInstance();

                var parameterNames = PooledHashSet<string>.GetInstance();
                if (isIteratorOrAsyncMethod)
                {
                    Debug.Assert(IsDisplayClassType(containingType));

                    foreach (var field in containingType.GetMembers().OfType<FieldSymbol>())
                    {
                        // All iterator and async state machine fields (including hoisted locals) have mangled names, except
                        // for hoisted parameters (whose field names are always the same as the original source parameters).
                        var fieldName = field.Name;
                        if (GeneratedNames.GetKind(fieldName) == GeneratedNameKind.None)
                        {
                            parameterNames.Add(fieldName);
                        }
                    }
                }
                else
                {
                    foreach (var p in method.Parameters)
                    {
                        parameterNames.Add(p.Name);
                    }
                }

                var pooledHoistedParameterNames = PooledHashSet<string>.GetInstance();
                foreach (var instance in displayClassInstances)
                {
                    GetDisplayClassVariables(
                        displayClassVariableNamesInOrderBuilder,
                        displayClassVariablesBuilder,
                        parameterNames,
                        inScopeHoistedLocals,
                        instance,
                        pooledHoistedParameterNames);
                }

                hoistedParameterNames = pooledHoistedParameterNames.ToImmutableHashSet<string>();
                pooledHoistedParameterNames.Free();
                parameterNames.Free();

                displayClassVariableNamesInOrder = displayClassVariableNamesInOrderBuilder.ToImmutableAndFree();
                displayClassVariables = displayClassVariablesBuilder.ToImmutableDictionary();
                displayClassVariablesBuilder.Free();
            }
            else
            {
                hoistedParameterNames = ImmutableHashSet<string>.Empty;
                displayClassVariableNamesInOrder = ImmutableArray<string>.Empty;
                displayClassVariables = ImmutableDictionary<string, DisplayClassVariable>.Empty;
            }

            displayClassTypes.Free();
            displayClassInstances.Free();
        }