/// <summary> /// Returns an index of a slot that stores specified hoisted local variable in the previous generation. /// </summary> public abstract bool TryGetPreviousHoistedLocalSlotIndex( SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, DiagnosticBag diagnostics, out int slotIndex);
public int AssignLocalOrdinal(SynthesizedLocalKind localKind, int syntaxOffset) { #if !DEBUG // Optimization (avoid growing the dictionary below): // User-defined locals have to have a distinct syntax offset, thus ordinal is always 0. if (localKind == SynthesizedLocalKind.UserDefined) { return 0; } #endif int ordinal; long key = MakeKey(localKind, syntaxOffset); // Group by syntax offset and kind. // Variables associated with the same syntax and kind will be assigned different ordinals. if (_lazyMap == null) { _lazyMap = PooledDictionary<long, int>.GetInstance(); ordinal = 0; } else if (!_lazyMap.TryGetValue(key, out ordinal)) { ordinal = 0; } _lazyMap[key] = ordinal + 1; Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined); return ordinal; }
internal SynthesizedLocal WithSynthesizedLocalKind(SynthesizedLocalKind kind) { return new SynthesizedLocal( this.containingMethodOpt, this.type, kind, this.syntaxOpt, this.isPinned, this.refKind); }
internal SynthesizedLocal WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { return new SynthesizedLocal( _containingMethodOpt, _type, kind, syntax, _isPinned, _refKind); }
public EncHoistedLocalMetadata(string name, Cci.ITypeReference type, SynthesizedLocalKind synthesizedKind) { Debug.Assert(name != null); Debug.Assert(type != null); Debug.Assert(synthesizedKind.IsLongLived()); this.Name = name; this.Type = type; this.SynthesizedKind = synthesizedKind; }
public abstract LocalDefinition GetPreviousLocal( Cci.ITypeReference type, ILocalSymbolInternal symbol, string nameOpt, SynthesizedLocalKind kind, LocalDebugId id, LocalVariableAttributes pdbAttributes, LocalSlotConstraints constraints, bool isDynamic, ImmutableArray<TypedConstant> dynamicTransformFlags);
internal SynthesizedLocal( MethodSymbol containingMethodOpt, TypeSymbol type, SynthesizedLocalKind kind, SyntaxNode syntaxOpt = null, bool isPinned = false, RefKind refKind = RefKind.None) { _containingMethodOpt = containingMethodOpt; _type = type; _kind = kind; _syntaxOpt = syntaxOpt; _isPinned = isPinned; _refKind = refKind; }
internal SynthesizedLocal( MethodSymbol containingMethodOpt, TypeSymbol type, SynthesizedLocalKind kind, CSharpSyntaxNode syntaxOpt = null, bool isPinned = false, RefKind refKind = RefKind.None) { this.containingMethodOpt = containingMethodOpt; this.type = type; this.kind = kind; this.syntaxOpt = syntaxOpt; this.isPinned = isPinned; this.refKind = refKind; }
internal SynthesizedLocal( MethodSymbol containingMethodOpt, TypeSymbol type, SynthesizedLocalKind kind, CSharpSyntaxNode syntaxOpt = null, bool isPinned = false, RefKind refKind = RefKind.None, [CallerLineNumber]int createdAtLineNumber = 0, [CallerFilePath]string createdAtFilePath = null) { Debug.Assert(type.SpecialType != SpecialType.System_Void); this.containingMethodOpt = containingMethodOpt; this.type = type; this.kind = kind; this.syntaxOpt = syntaxOpt; this.isPinned = isPinned; this.refKind = refKind; this.createdAtLineNumber = createdAtLineNumber; this.createdAtFilePath = createdAtFilePath; }
/// <summary> /// Creates a new LocalDefinition. /// </summary> /// <param name="symbolOpt">Local symbol, used by edit and continue only, null otherwise.</param> /// <param name="nameOpt">Name associated with the slot.</param> /// <param name="type">Type associated with the slot.</param> /// <param name="slot">Slot position in the signature.</param> /// <param name="synthesizedKind">Local kind.</param> /// <param name="id">Local id.</param> /// <param name="pdbAttributes">Value to emit in the attributes field in the PDB.</param> /// <param name="constraints">Specifies whether slot type should have pinned modifier and whether slot should have byref constraint.</param> /// <param name="dynamicTransformFlags">The synthesized dynamic attributes of the local.</param> /// <param name="tupleElementNames">Tuple element names of the local.</param> public LocalDefinition( ILocalSymbol symbolOpt, string nameOpt, Cci.ITypeReference type, int slot, SynthesizedLocalKind synthesizedKind, LocalDebugId id, LocalVariableAttributes pdbAttributes, LocalSlotConstraints constraints, ImmutableArray<TypedConstant> dynamicTransformFlags, ImmutableArray<TypedConstant> tupleElementNames) { _symbolOpt = symbolOpt; _nameOpt = nameOpt; _type = type; _slot = slot; _slotInfo = new LocalSlotDebugInfo(synthesizedKind, id); _pdbAttributes = pdbAttributes; _dynamicTransformFlags = dynamicTransformFlags.NullToEmpty(); _tupleElementNames = tupleElementNames.NullToEmpty(); _constraints = constraints; }
/// <summary> /// Creates a new LocalDefinition. /// </summary> /// <param name="symbolOpt">Local symbol, used by edit and continue only, null otherwise.</param> /// <param name="nameOpt">Name associated with the slot.</param> /// <param name="type">Type associated with the slot.</param> /// <param name="slot">Slot position in the signature.</param> /// <param name="dynamicTransformFlags">Contains the synthesized dynamic attributes of the local</param> /// <param name="synthesizedKind">Local kind.</param> /// <param name="id">Local id.</param> /// <param name="pdbAttributes">Value to emit in the attributes field in the PDB.</param> /// <param name="constraints">Specifies whether slot type should have pinned modifier and whether slot should have byref constraint.</param> /// <param name="isDynamic">Specifies if the type is Dynamic.</param> public LocalDefinition( ILocalSymbol symbolOpt, string nameOpt, Cci.ITypeReference type, int slot, SynthesizedLocalKind synthesizedKind, LocalDebugId id, uint pdbAttributes, LocalSlotConstraints constraints, bool isDynamic, ImmutableArray<TypedConstant> dynamicTransformFlags) { _symbolOpt = symbolOpt; _nameOpt = nameOpt; _type = type; _slot = slot; _slotInfo = new LocalSlotDebugInfo(synthesizedKind, id); _pdbAttributes = pdbAttributes; _dynamicTransformFlags = dynamicTransformFlags; _constraints = constraints; _isDynamic = isDynamic; }
internal SynthesizedLocal( MethodSymbol containingMethodOpt, TypeSymbol type, SynthesizedLocalKind kind, SyntaxNode syntaxOpt = null, bool isPinned = false, RefKind refKind = RefKind.None, [CallerLineNumber]int createdAtLineNumber = 0, [CallerFilePath]string createdAtFilePath = null) { Debug.Assert(type.SpecialType != SpecialType.System_Void); Debug.Assert(!kind.IsLongLived() || syntaxOpt != null); _containingMethodOpt = containingMethodOpt; _type = type; _kind = kind; _syntaxOpt = syntaxOpt; _isPinned = isPinned; _refKind = refKind; _createdAtLineNumber = createdAtLineNumber; _createdAtFilePath = createdAtFilePath; }
public override LocalDefinition GetPreviousLocal( Cci.ITypeReference currentType, ILocalSymbolInternal currentLocalSymbol, string nameOpt, SynthesizedLocalKind kind, LocalDebugId id, uint pdbAttributes, LocalSlotConstraints constraints, bool isDynamic, ImmutableArray<TypedConstant> dynamicTransformFlags) { if (id.IsNone) { return null; } LocalDebugId previousId; if (!TryGetPreviousLocalId(currentLocalSymbol.GetDeclaratorSyntax(), id, out previousId)) { return null; } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { return null; } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncLocalInfo(new LocalSlotDebugInfo(kind, previousId), previousType, constraints, signature: null); int slot; if (!_previousLocalSlots.TryGetValue(localKey, out slot)) { return null; } return new LocalDefinition( currentLocalSymbol, nameOpt, currentType, slot, kind, id, pdbAttributes, constraints, isDynamic, dynamicTransformFlags); }
public StateMachineFieldSymbol StateMachineField(TypeSymbol type, string name, SynthesizedLocalKind synthesizedKind, int slotIndex) { var result = new StateMachineFieldSymbol(CurrentType, type, name, synthesizedKind, slotIndex, isPublic: false); AddField(CurrentType, result); return result; }
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string name, SynthesizedLocalKind synthesizedKind, int slotIndex, bool isPublic) : this(stateMachineType, type, name, new LocalSlotDebugInfo(synthesizedKind, LocalDebugId.None), slotIndex, isPublic: isPublic) { }
public static bool IsSlotReusable(this SynthesizedLocalKind kind, OptimizationLevel optimizations) { return(kind.IsSlotReusable(optimizations != OptimizationLevel.Release)); }
public override bool TryGetPreviousHoistedLocalSlotIndex(SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, out int slotIndex) { Debug.Assert(_hoistedLocalSlotsOpt != null); LocalDebugId previousId; if (!TryGetPreviousLocalId(currentDeclarator, currentId, out previousId)) { slotIndex = -1; return(false); } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { slotIndex = -1; return(false); } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncHoistedLocalInfo(new LocalSlotDebugInfo(synthesizedKind, previousId), previousType); return(_hoistedLocalSlotsOpt.TryGetValue(localKey, out slotIndex)); }
private void AddSynthesizedLocal(SynthesizedLocalKind synthesizedKind) { AddLocalImpl(ref synthesizedSlotIndex, synthesizedKind); }
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { throw ExceptionUtilities.Unreachable; }
public LocalSymbol SynthesizedLocal(TypeSymbol type, CSharpSyntaxNode syntax = null, bool isPinned = false, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp) { return(new SynthesizedLocal(CurrentMethod, type, kind, syntax, isPinned, refKind)); }
public LocalName(SynthesizedLocalKind kind, int uniqueId) { this.UserDefinedName = null; this.Kind = kind; this.UniqueId = uniqueId; }
public StateMachineFieldSymbol(NamedTypeSymbol stateMachineType, TypeSymbol type, string name, SynthesizedLocalKind synthesizedKind, int slotIndex, bool isPublic) : this(stateMachineType, type, name, new LocalSlotDebugInfo(synthesizedKind, LocalDebugId.None), slotIndex, isPublic : isPublic) { }
public override LocalDefinition?GetPreviousLocal( Cci.ITypeReference currentType, ILocalSymbolInternal currentLocalSymbol, string?name, SynthesizedLocalKind kind, LocalDebugId id, LocalVariableAttributes pdbAttributes, LocalSlotConstraints constraints, ImmutableArray <bool> dynamicTransformFlags, ImmutableArray <string> tupleElementNames ) { if (id.IsNone) { return(null); } if ( !TryGetPreviousLocalId( currentLocalSymbol.GetDeclaratorSyntax(), id, out LocalDebugId previousId ) ) { return(null); } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { return(null); } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncLocalInfo( new LocalSlotDebugInfo(kind, previousId), previousType, constraints, signature: null ); if (!_previousLocalSlots.TryGetValue(localKey, out int slot)) { return(null); } return(new LocalDefinition( currentLocalSymbol, name, currentType, slot, kind, id, pdbAttributes, constraints, dynamicTransformFlags, tupleElementNames )); }
public override bool TryGetPreviousHoistedLocalSlotIndex( SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, DiagnosticBag diagnostics, out int slotIndex) { // Well-formed state machine attribute wasn't found in the baseline (the type is missing or bad). // Should rarely happen since the IDE reports a rude edit if the attribute type doesn't exist. if (_hoistedLocalSlotsOpt == null) { // TODO: better error message https://github.com/dotnet/roslyn/issues/9196 diagnostics.Add(_messageProvider.CreateDiagnostic(_messageProvider.ERR_ModuleEmitFailure, NoLocation.Singleton, _previousTopLevelMethod.ContainingModule.Name)); slotIndex = -1; return false; } LocalDebugId previousId; if (!TryGetPreviousLocalId(currentDeclarator, currentId, out previousId)) { slotIndex = -1; return false; } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { slotIndex = -1; return false; } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncHoistedLocalInfo(new LocalSlotDebugInfo(synthesizedKind, previousId), previousType); return _hoistedLocalSlotsOpt.TryGetValue(localKey, out slotIndex); }
internal static string MakeHoistedLocalFieldName(SynthesizedLocalKind kind, int slotIndex, string localNameOpt = null) { Debug.Assert((localNameOpt != null) == (kind == SynthesizedLocalKind.UserDefined)); Debug.Assert(slotIndex >= 0); Debug.Assert(kind.IsLongLived()); // Lambda display class local follows a different naming pattern. // EE depends on the name format. // There's logic in the EE to recognize locals that have been captured by a lambda // and would have been hoisted for the state machine. Basically, we just hoist the local containing // the instance of the lambda display class and retain its original name (rather than using an // iterator local name). See FUNCBRECEE::ImportIteratorMethodInheritedLocals. var result = PooledStringBuilder.GetInstance(); var builder = result.Builder; builder.Append('<'); if (localNameOpt != null) { Debug.Assert(localNameOpt.IndexOf('.') == -1); builder.Append(localNameOpt); } builder.Append('>'); if (kind == SynthesizedLocalKind.LambdaDisplayClass) { builder.Append((char)GeneratedNameKind.DisplayClassLocalOrField); } else if (kind == SynthesizedLocalKind.UserDefined) { builder.Append((char)GeneratedNameKind.HoistedLocalField); } else { builder.Append((char)GeneratedNameKind.HoistedSynthesizedLocalField); } builder.Append("__"); builder.Append(slotIndex + 1); return result.ToStringAndFree(); }
private static long MakeKey(SynthesizedLocalKind localKind, int syntaxOffset) { return((long)syntaxOffset << 8 | (long)localKind); }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal(containingMethod, type, kind, syntax: kind.IsLongLived() ? syntax : null, refKind: refKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return local; }
/// <summary> /// Returns an index of a slot that stores specified hoisted local variable in the previous generation, /// or -1 if there is no such slot. /// </summary> public abstract int GetPreviousHoistedLocalSlotIndex( SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId);
// Matches names generated by Dev11. internal static string MakeLocalName(SynthesizedLocalKind kind, int uniqueId) { Debug.Assert(kind.IsLongLived()); if (kind == SynthesizedLocalKind.CachedAnonymousMethodDelegate) { // TODO: consider removing this special case, EE doesn't depend on the name. return SynthesizedLocalNamePrefix + "<>9__CachedAnonymousMethodDelegate" + uniqueId; } if (kind == SynthesizedLocalKind.LambdaDisplayClass) { // Lambda display class local follows a different naming pattern. // EE depends on the name format. return MakeLambdaDisplayClassStorageName(uniqueId); } return string.Format(SynthesizedLocalNamePrefix + "{0}${1:0000}", (int)kind, uniqueId); }
private BoundExpression HoistExpression( BoundExpression expr, AwaitExpressionSyntax awaitSyntaxOpt, int syntaxOffset, bool isRef, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <StateMachineFieldSymbol> hoistedFields, ref bool needsSacrificialEvaluation) { switch (expr.Kind) { case BoundKind.ArrayAccess: { var array = (BoundArrayAccess)expr; BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation); var indices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, false, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); } needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions return(array.Update(expression, indices.ToImmutableAndFree(), array.Type)); } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)expr; if (field.FieldSymbol.IsStatic) { // the address of a static field, and the value of a readonly static field, is stable if (isRef || field.FieldSymbol.IsReadOnly) { return(expr); } goto default; } if (!isRef) { goto default; } var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct, sideEffects, hoistedFields, ref needsSacrificialEvaluation); if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) { needsSacrificialEvaluation = true; // need the null check in field receiver } return(F.Field(receiver, field.FieldSymbol)); } case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.DefaultOperator: return(expr); default: if (expr.ConstantValue != null) { return(expr); } if (isRef) { throw ExceptionUtilities.UnexpectedValue(expr.Kind); } TypeSymbol fieldType = expr.Type; StateMachineFieldSymbol hoistedField; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; Debug.Assert(awaitSyntaxOpt != null); int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); var id = new LocalDebugId(syntaxOffset, ordinal); // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. // However, it may happen that the type changes, in which case we need to allocate a new slot. int slotIndex; if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( awaitSyntaxOpt, F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics), kind, id, Diagnostics, out slotIndex)) { slotIndex = _nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); } else { hoistedField = GetOrAllocateReusableHoistedField(fieldType); } hoistedFields.Add(hoistedField); var replacement = F.Field(F.This(), hoistedField); sideEffects.Add(F.AssignmentExpression(replacement, expr)); return(replacement); } }
public override bool TryGetPreviousHoistedLocalSlotIndex(SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, DiagnosticBag diagnostics, out int slotIndex) { slotIndex = -1; return(false); }
internal abstract LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax);
public static bool IsLongLived(this SynthesizedLocalKind kind) { return(kind >= SynthesizedLocalKind.UserDefined); }
public LocalSlotDebugInfo(SynthesizedLocalKind synthesizedKind, LocalDebugId id) { this.SynthesizedKind = synthesizedKind; this.Id = id; }
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { throw ExceptionUtilities.Unreachable; }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp(BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal(containingMethod, type, kind, syntax: kind.IsLongLived() ? syntax : null, refKind: refKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return(local); }
/// <summary> /// Takes an expression and returns the bound local expression "temp" /// and the bound assignment expression "temp = expr". /// </summary> public BoundLocal StoreToTemp( BoundExpression argument, out BoundAssignmentOperator store, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp, CSharpSyntaxNode syntaxOpt = null #if DEBUG , [CallerLineNumber]int callerLineNumber = 0 , [CallerFilePath]string callerFilePath = null #endif ) { MethodSymbol containingMethod = this.CurrentMethod; var syntax = argument.Syntax; var type = argument.Type; var local = new BoundLocal( syntax, new SynthesizedLocal( containingMethod, type, kind, #if DEBUG createdAtLineNumber: callerLineNumber, createdAtFilePath: callerFilePath, #endif syntaxOpt: syntaxOpt ?? (kind.IsLongLived() ? syntax : null), isPinned: false, refKind: refKind), null, type); store = new BoundAssignmentOperator( syntax, local, argument, refKind, type); return local; }
public static uint PdbAttributes(this SynthesizedLocalKind kind) { return((SynthesizedLocalKind.FirstIgnoredByExpressionCompiler <= kind && kind <= SynthesizedLocalKind.LastIgnoredByExpressionCompiler) ? Cci.PdbWriter.HiddenLocalAttributesValue : Cci.PdbWriter.DefaultLocalAttributesValue); }
public LocalSymbol SynthesizedLocal( TypeSymbol type, SyntaxNode syntax = null, bool isPinned = false, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp, [CallerLineNumber]int createdAtLineNumber = 0, [CallerFilePath]string createdAtFilePath = null) { return new SynthesizedLocal(CurrentMethod, type, kind, syntax, isPinned, refKind, createdAtLineNumber, createdAtFilePath); }
public LocalName(string name, SynthesizedLocalKind kind, int uniqueId) { this.Name = name; this.Kind = kind; this.UniqueId = uniqueId; }
public override bool TryGetPreviousHoistedLocalSlotIndex(SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, out int slotIndex) { Debug.Assert(_hoistedLocalSlotsOpt != null); LocalDebugId previousId; if (!TryGetPreviousLocalId(currentDeclarator, currentId, out previousId)) { slotIndex = -1; return false; } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { slotIndex = -1; return false; } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncHoistedLocalInfo(new LocalSlotDebugInfo(synthesizedKind, previousId), previousType); return _hoistedLocalSlotsOpt.TryGetValue(localKey, out slotIndex); }
public override bool TryGetPreviousHoistedLocalSlotIndex( SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, DiagnosticBag diagnostics, out int slotIndex) { // The previous method was not a state machine (it is allowed to change non-state machine to a state machine): if (_hoistedLocalSlotsOpt == null) { slotIndex = -1; return false; } LocalDebugId previousId; if (!TryGetPreviousLocalId(currentDeclarator, currentId, out previousId)) { slotIndex = -1; return false; } var previousType = _symbolMap.MapReference(currentType); if (previousType == null) { slotIndex = -1; return false; } // TODO (bug #781309): Should report a warning if the type of the local has changed // and the previous value will be dropped. var localKey = new EncHoistedLocalInfo(new LocalSlotDebugInfo(synthesizedKind, previousId), previousType); return _hoistedLocalSlotsOpt.TryGetValue(localKey, out slotIndex); }
internal static string MakeSynthesizedLocalName(SynthesizedLocalKind kind, ref int uniqueId) { Debug.Assert(kind.IsLongLived()); // Lambda display class local has to be named. EE depends on the name format. if (kind == SynthesizedLocalKind.LambdaDisplayClass) { return MakeLambdaDisplayLocalName(uniqueId++); } return null; }
private BoundExpression HoistExpression( BoundExpression expr, AwaitExpressionSyntax awaitSyntaxOpt, int syntaxOffset, RefKind refKind, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <StateMachineFieldSymbol> hoistedFields, ref bool needsSacrificialEvaluation) { switch (expr.Kind) { case BoundKind.ArrayAccess: { var array = (BoundArrayAccess)expr; BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); var indices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); } needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions return(array.Update(expression, indices.ToImmutableAndFree(), array.Type)); } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)expr; if (field.FieldSymbol.IsStatic) { // the address of a static field, and the value of a readonly static field, is stable if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) { return(expr); } goto default; } if (refKind == RefKind.None) { goto default; } var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) { needsSacrificialEvaluation = true; // need the null check in field receiver } return(F.Field(receiver, field.FieldSymbol)); } case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.DefaultExpression: return(expr); case BoundKind.Call: var call = (BoundCall)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.In) { Debug.Assert(call.Method.RefKind != RefKind.None); F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method); } // method call is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) { Debug.Assert(conditional.IsRef); F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location); } // conditional expr is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; default: if (expr.ConstantValue != null) { return(expr); } if (refKind != RefKind.None) { throw ExceptionUtilities.UnexpectedValue(expr.Kind); } TypeSymbol fieldType = expr.Type; StateMachineFieldSymbol hoistedField; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; Debug.Assert(awaitSyntaxOpt != null); int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); var id = new LocalDebugId(syntaxOffset, ordinal); // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. // However, it may happen that the type changes, in which case we need to allocate a new slot. int slotIndex; if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( awaitSyntaxOpt, F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics), kind, id, Diagnostics, out slotIndex)) { slotIndex = _nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); } else { hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _); } hoistedFields.Add(hoistedField); var replacement = F.Field(F.This(), hoistedField); sideEffects.Add(F.AssignmentExpression(replacement, expr)); return(replacement); } }
public LocalSymbol SynthesizedLocal(TypeSymbol type, CSharpSyntaxNode syntax = null, bool isPinned = false, RefKind refKind = RefKind.None, SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp) { return new SynthesizedLocal(CurrentMethod, type, kind, syntax, isPinned, refKind); }
public override LocalDefinition GetPreviousLocal( Cci.ITypeReference type, ILocalSymbolInternal symbol, string nameOpt, SynthesizedLocalKind synthesizedKind, LocalDebugId id, uint pdbAttributes, LocalSlotConstraints constraints, bool isDynamic, ImmutableArray<TypedConstant> dynamicTransformFlags) { var local = symbol as EELocalSymbol; if ((object)local == null) { return null; } return _locals[local.Ordinal]; }
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { throw new NotImplementedException(); }
public override bool TryGetPreviousHoistedLocalSlotIndex(SyntaxNode currentDeclarator, Cci.ITypeReference currentType, SynthesizedLocalKind synthesizedKind, LocalDebugId currentId, DiagnosticBag diagnostics, out int slotIndex) { slotIndex = -1; return false; }
internal static bool TryParseLocalName(string name, out SynthesizedLocalKind kind, out int uniqueId) { if (name.StartsWith(SynthesizedLocalNamePrefix, StringComparison.Ordinal)) { name = name.Substring(SynthesizedLocalNamePrefix.Length); int separator = name.IndexOf('$'); if (separator > 0) { int k; int n; if (int.TryParse(name.Substring(0, separator), NumberStyles.None, CultureInfo.InvariantCulture, out k) && int.TryParse(name.Substring(separator + 1), NumberStyles.None, CultureInfo.InvariantCulture, out n)) { kind = (SynthesizedLocalKind)k; uniqueId = n; return true; } } } kind = SynthesizedLocalKind.None; uniqueId = 0; return false; }
public LocalName(string name) { this.UserDefinedName = name; this.Kind = SynthesizedLocalKind.None; this.UniqueId = 0; }