private void MakeInitialProxy(TypeMap TypeMap, MultiDictionary <Symbol, CSharpSyntaxNode> locations, LocalSymbol local) { Debug.Assert(local.RefKind == RefKind.None); CapturedSymbolReplacement result = new CapturedToFrameSymbolReplacement(MakeHoistedField(TypeMap, local, local.Type)); if (local.Type.IsRestrictedType()) { foreach (CSharpSyntaxNode syntax in locations[local]) { // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, local.Type); } } variableProxies.Add(local, result); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundNode PossibleIteratorScope(ImmutableArray<LocalSymbol> locals, Func<BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) return wrapped(); var proxyFields = ArrayBuilder<SynthesizedFieldSymbolBase>.GetInstance(); foreach (var local in locals) { if (!VariablesCaptured.Contains(local)) continue; CapturedSymbolReplacement proxy; if (proxies.TryGetValue(local, out proxy)) { // All of the user-declared variables have pre-allocated proxies var field = proxy.HoistedField; Debug.Assert((object)field != null); Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) proxyFields.Add(field); } else { if (local.RefKind == RefKind.None) { SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type)); proxy = new CapturedToFrameSymbolReplacement(field); proxies.Add(local, proxy); } // ref temporary variables have proxies that are allocated on demand // See VisitAssignmentOperator. } } var translatedStatement = wrapped(); // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals var clearTemps = ArrayBuilder<BoundAssignmentOperator>.GetInstance(); foreach (var local in locals) { ArrayBuilder<SynthesizedFieldSymbolBase> frees; if (freeTempsMap.TryGetValue(local, out frees)) { Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way freeTempsMap.Remove(local); foreach (var field in frees) { if (MightContainReferences(field.Type)) { clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } FreeTemp(field); } frees.Free(); } } if (clearTemps.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable<BoundStatement>()) ); } clearTemps.Free(); // wrap the node in an iterator scope for debugging if (proxyFields.Count != 0) { translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement); } proxyFields.Free(); return translatedStatement; }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!VariablesCaptured.Contains(local)) { continue; } CapturedSymbolReplacement proxy; if (proxies.TryGetValue(local, out proxy)) { // All of the user-declared variables have pre-allocated proxies var field = proxy.HoistedField; Debug.Assert((object)field != null); Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) { proxyFields.Add(field); } } else { if (local.RefKind == RefKind.None) { SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type)); proxy = new CapturedToFrameSymbolReplacement(field); proxies.Add(local, proxy); } // ref temporary variables have proxies that are allocated on demand // See VisitAssignmentOperator. } } var translatedStatement = wrapped(); // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); foreach (var local in locals) { ArrayBuilder <SynthesizedFieldSymbolBase> frees; if (freeTempsMap.TryGetValue(local, out frees)) { Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way freeTempsMap.Remove(local); foreach (var field in frees) { if (MightContainReferences(field.Type)) { clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } FreeTemp(field); } frees.Free(); } } if (clearTemps.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>()) ); } clearTemps.Free(); // wrap the node in an iterator scope for debugging if (proxyFields.Count != 0) { translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement); } proxyFields.Free(); return(translatedStatement); }
private void MakeInitialProxy(TypeMap TypeMap, MultiDictionary<Symbol, CSharpSyntaxNode> locations, LocalSymbol local) { Debug.Assert(local.RefKind == RefKind.None); CapturedSymbolReplacement result = new CapturedToFrameSymbolReplacement(MakeHoistedLocalField(TypeMap, local, local.Type)); if (local.Type.IsRestrictedType()) { foreach (CSharpSyntaxNode syntax in locations[local]) { // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, local.Type); } } variableProxies.Add(local, result); }
/// <summary> /// Introduce a frame around the translation of the given node. /// </summary> /// <param name="node">The node whose translation should be translated to contain a frame</param> /// <param name="frame">The frame for the translated node</param> /// <param name="F">A function that computes the translation of the node. It receives lists of added statements and added symbols</param> /// <returns>The translated statement, as returned from F</returns> private T IntroduceFrame <T>(BoundNode node, LambdaFrame frame, Func <ArrayBuilder <BoundExpression>, ArrayBuilder <LocalSymbol>, T> F) { NamedTypeSymbol frameType = frame.ConstructIfGeneric(StaticCast <TypeSymbol> .From(currentTypeParameters)); LocalSymbol framePointer = new LambdaFrameLocalSymbol(this.topLevelMethod, frameType, CompilationState); CSharpSyntaxNode syntax = node.Syntax; // assign new frame to the frame variable CompilationState.AddSynthesizedMethod(frame.Constructor, FlowAnalysisPass.AppendImplicitReturn(MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null), frame.Constructor)); var prologue = ArrayBuilder <BoundExpression> .GetInstance(); MethodSymbol constructor = frame.Constructor.AsMember(frameType); Debug.Assert(frameType == constructor.ContainingType); var newFrame = new BoundObjectCreationExpression( syntax: syntax, constructor: constructor); prologue.Add(new BoundAssignmentOperator(syntax, new BoundLocal(syntax, framePointer, null, frameType), newFrame, frameType)); CapturedSymbolReplacement oldInnermostFrameProxy = null; if ((object)innermostFramePointer != null) { proxies.TryGetValue(innermostFramePointer, out oldInnermostFrameProxy); if (analysis.needsParentFrame.Contains(node)) { var capturedFrame = new LambdaCapturedVariable(frame, innermostFramePointer); FieldSymbol frameParent = capturedFrame.AsMember(frameType); BoundExpression left = new BoundFieldAccess(syntax, new BoundLocal(syntax, framePointer, null, frameType), frameParent, null); BoundExpression right = FrameOfType(syntax, frameParent.Type as NamedTypeSymbol); BoundExpression assignment = new BoundAssignmentOperator(syntax, left, right, left.Type); if (this.currentMethod.MethodKind == MethodKind.Constructor && capturedFrame.Type == this.currentMethod.ContainingType && !this.seenBaseCall) { // Containing method is a constructor // Initialization statement for the "this" proxy must be inserted // after the constructor initializer statement block // This insertion will be done by the delegate F Debug.Assert(thisProxyInitDeferred == null); thisProxyInitDeferred = assignment; } else { prologue.Add(assignment); } if (CompilationState.Emitting) { CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(frame, capturedFrame); } proxies[innermostFramePointer] = new CapturedToFrameSymbolReplacement(capturedFrame); } } // Capture any parameters of this block. This would typically occur // at the top level of a method or lambda with captured parameters. // TODO: speed up the following by computing it in analysis. foreach (var v in analysis.variablesCaptured) { BoundNode varNode; if (!analysis.variableBlock.TryGetValue(v, out varNode) || varNode != node || analysis.declaredInsideExpressionLambda.Contains(v)) { continue; } InitVariableProxy(syntax, v, framePointer, prologue); } Symbol oldInnermostFramePointer = innermostFramePointer; innermostFramePointer = framePointer; var addedLocals = ArrayBuilder <LocalSymbol> .GetInstance(); addedLocals.Add(framePointer); framePointers.Add(frame, framePointer); var result = F(prologue, addedLocals); framePointers.Remove(frame); innermostFramePointer = oldInnermostFramePointer; if ((object)innermostFramePointer != null) { if (oldInnermostFrameProxy != null) { proxies[innermostFramePointer] = oldInnermostFrameProxy; } else { proxies.Remove(innermostFramePointer); } } return(result); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundStatement PossibleIteratorScope(ImmutableArray<LocalSymbol> locals, Func<BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return wrapped(); } var hoistedUserDefinedLocals = ArrayBuilder<SynthesizedFieldSymbolBase>.GetInstance(); foreach (var local in locals) { if (!NeedsProxy(local)) { continue; } // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator. if (local.RefKind != RefKind.None) { Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.AwaitSpill); continue; } Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.None || local.SynthesizedLocalKind.IsLongLived()); CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { proxy = new CapturedToFrameSymbolReplacement(GetOrAllocateHoistedField(TypeMap.SubstituteType(local.Type)), isReusable: true); proxies.Add(local, proxy); } if (local.SynthesizedLocalKind == SynthesizedLocalKind.None) { hoistedUserDefinedLocals.Add(((CapturedToFrameSymbolReplacement)proxy).HoistedField); } } var translatedStatement = wrapped(); var variableCleanup = ArrayBuilder<BoundAssignmentOperator>.GetInstance(); // produce cleanup code for all fields of locals defined by this block // as well as all proxies allocated by VisitAssignmentOperator within this block: foreach (var local in locals) { CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { continue; } var simpleProxy = proxy as CapturedToFrameSymbolReplacement; if (simpleProxy != null) { AddVariableCleanup(variableCleanup, simpleProxy.HoistedField); if (proxy.IsReusable) { FreeHoistedField(simpleProxy.HoistedField); } } else { foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) { AddVariableCleanup(variableCleanup, field); if (proxy.IsReusable) { FreeHoistedField(field); } } } } if (variableCleanup.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F))); } variableCleanup.Free(); // wrap the node in an iterator scope for debugging if (hoistedUserDefinedLocals.Count != 0) { translatedStatement = F.Block(new BoundIteratorScope(F.Syntax, hoistedUserDefinedLocals.ToImmutable(), translatedStatement)); } hoistedUserDefinedLocals.Free(); return translatedStatement; }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var hoistedUserDefinedLocals = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!NeedsProxy(local)) { continue; } // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator. if (local.RefKind != RefKind.None) { Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.AwaitSpill); continue; } Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.None || local.SynthesizedLocalKind.IsLongLived()); CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { proxy = new CapturedToFrameSymbolReplacement(GetOrAllocateHoistedField(TypeMap.SubstituteType(local.Type)), isReusable: true); proxies.Add(local, proxy); } if (local.SynthesizedLocalKind == SynthesizedLocalKind.None) { hoistedUserDefinedLocals.Add(((CapturedToFrameSymbolReplacement)proxy).HoistedField); } } var translatedStatement = wrapped(); var variableCleanup = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); // produce cleanup code for all fields of locals defined by this block // as well as all proxies allocated by VisitAssignmentOperator within this block: foreach (var local in locals) { CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { continue; } var simpleProxy = proxy as CapturedToFrameSymbolReplacement; if (simpleProxy != null) { AddVariableCleanup(variableCleanup, simpleProxy.HoistedField); if (proxy.IsReusable) { FreeHoistedField(simpleProxy.HoistedField); } } else { foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) { AddVariableCleanup(variableCleanup, field); if (proxy.IsReusable) { FreeHoistedField(field); } } } } if (variableCleanup.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F))); } variableCleanup.Free(); // wrap the node in an iterator scope for debugging if (hoistedUserDefinedLocals.Count != 0) { translatedStatement = F.Block(new BoundIteratorScope(F.Syntax, hoistedUserDefinedLocals.ToImmutable(), translatedStatement)); } hoistedUserDefinedLocals.Free(); return(translatedStatement); }