/// <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)); }
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; }
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)); } } }
/// <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(); }