public override void LeaveTryStatement(TryStatement node) { TryStatementInfo info = _tryStatementStack.Pop(); if (info._containsYield) { ReplaceCurrentNode(info._replacement); TryStatementInfo currentTry = (_tryStatementStack.Count > 0) ? _tryStatementStack.Peek() : null; info._replacement.Add(node.ProtectedBlock); if (currentTry != null) { ConvertTryStatement(currentTry); info._replacement.Add(SetStateTo(currentTry._stateNumber)); } else { // leave try block, reset state to prevent ensure block from being called again info._replacement.Add(SetStateTo(_finishedStateNumber)); } BooMethodBuilder ensureMethod = _enumerator.AddMethod("$ensure" + info._stateNumber, TypeSystemServices.VoidType, TypeMemberModifiers.Private); ensureMethod.Body.Add(info._statement.EnsureBlock); info._ensureMethod = ensureMethod.Entity; info._replacement.Add(CallMethodOnSelf(ensureMethod.Entity)); _convertedTryStatements.Add(info); } }
public override bool EnterTryStatement(TryStatement node) { TryStatementInfo info = new TryStatementInfo(); info._statement = node; if (_tryStatementStack.Count > 0) { info._parent = _tryStatementStack.Peek(); } _tryStatementStack.Push(info); return(true); }
IMethod CreateDisposeMethod() { BooMethodBuilder mn = _enumerator.AddVirtualMethod("Dispose", TypeSystemServices.VoidType); mn.Method.LexicalInfo = this.LexicalInfo; LabelStatement noEnsure = CodeBuilder.CreateLabel(_generator.Method, "noEnsure").LabelStatement; mn.Body.Add(noEnsure); mn.Body.Add(SetStateTo(_finishedStateNumber)); mn.Body.Add(new ReturnStatement()); // Create a section calling all ensure methods for each converted try block LabelStatement[] disposeLabels = new LabelStatement[_labels.Count]; for (int i = 0; i < _convertedTryStatements.Count; i++) { TryStatementInfo info = _convertedTryStatements[i]; disposeLabels[info._stateNumber] = CodeBuilder.CreateLabel(_generator.Method, "$ensure_" + info._stateNumber).LabelStatement; mn.Body.Add(disposeLabels[info._stateNumber]); mn.Body.Add(SetStateTo(_finishedStateNumber)); Block block = mn.Body; while (info._parent != null) { TryStatement ts = new TryStatement(); block.Add(ts); ts.ProtectedBlock.Add(CallMethodOnSelf(info._ensureMethod)); block = ts.EnsureBlock = new Block(); info = info._parent; } block.Add(CallMethodOnSelf(info._ensureMethod)); mn.Body.Add(new ReturnStatement()); } // now map the labels of the suspended states to the labels we just created for (int i = 0; i < _labels.Count; i++) { if (_tryStatementInfoForLabels[i] == null) { disposeLabels[i] = noEnsure; } else { disposeLabels[i] = disposeLabels[_tryStatementInfoForLabels[i]._stateNumber]; } } mn.Body.Insert(0, CodeBuilder.CreateSwitch( this.LexicalInfo, CodeBuilder.CreateMemberReference(_state), disposeLabels)); return(mn.Entity); }
void ConvertTryStatement(TryStatementInfo currentTry) { if (currentTry._containsYield) { return; } currentTry._containsYield = true; currentTry._stateNumber = _labels.Count; Block tryReplacement = new Block(); //tryReplacement.Add(CreateLabel(tryReplacement)); // when the MoveNext() is called while the enumerator is still in running state, don't jump to the // try block, but handle it like MoveNext() calls when the enumerator is in the finished state. _labels.Add(_labels[_finishedStateNumber]); _tryStatementInfoForLabels.Add(currentTry); tryReplacement.Add(SetStateTo(currentTry._stateNumber)); currentTry._replacement = tryReplacement; }
override public void LeaveYieldStatement(YieldStatement node) { TryStatementInfo currentTry = (_tryStatementStack.Count > 0) ? _tryStatementStack.Peek() : null; if (currentTry != null) { ConvertTryStatement(currentTry); } Block block = new Block(); block.Add( new ReturnStatement( node.LexicalInfo, CreateYieldInvocation(node.LexicalInfo, _labels.Count, node.Expression), null)); block.Add(CreateLabel(node)); // setting the state back to the "running" state not required, as that state has the same ensure blocks // as the state we are currently in. // if (currentTry != null) { // block.Add(SetStateTo(currentTry._stateNumber)); // } ReplaceCurrentNode(block); }
void ConvertTryStatement(TryStatementInfo currentTry) { if (currentTry._containsYield) return; currentTry._containsYield = true; currentTry._stateNumber = _labels.Count; Block tryReplacement = new Block(); //tryReplacement.Add(CreateLabel(tryReplacement)); // when the MoveNext() is called while the enumerator is still in running state, don't jump to the // try block, but handle it like MoveNext() calls when the enumerator is in the finished state. _labels.Add(_labels[_finishedStateNumber]); _tryStatementInfoForLabels.Add(currentTry); tryReplacement.Add(SetStateTo(currentTry._stateNumber)); currentTry._replacement = tryReplacement; }
public override bool EnterTryStatement(TryStatement node) { TryStatementInfo info = new TryStatementInfo(); info._statement = node; if (_tryStatementStack.Count > 0) info._parent = _tryStatementStack.Peek(); _tryStatementStack.Push(info); return true; }
private Block VisitAwaitExpression(AwaitExpression node, Expression resultPlace) { _seenAwait = true; var expression = Visit(node.BaseExpression); resultPlace = Visit(resultPlace); var getAwaiter = (IMethod)node["$GetAwaiter"]; var getResult = (IMethod)node["$GetResult"]; if (getAwaiter == null) { var resolveList = new List <IEntity>(); if (expression.ExpressionType.Resolve(resolveList, "GetAwaiter", EntityType.Method)) { getAwaiter = resolveList.Cast <IMethod>().First(m => m.GetParameters().Length == 0 && m.IsPublic); } else { throw CompilerErrorFactory.MissingGetAwaiter(expression); } getResult = getAwaiter.ReturnType.GetMembers().OfType <IMethod>().Single(m => m.Name.Equals("GetResult")); } Debug.Assert(getAwaiter != null && getResult != null); var isCompletedProp = getAwaiter.ReturnType.GetMembers().OfType <IProperty>().SingleOrDefault(p => p.Name.Equals("IsCompleted")); if (isCompletedProp == null) { var resolveList = new List <IEntity>(); if (getAwaiter.ReturnType.Resolve(resolveList, "IsCompleted", EntityType.Property)) { isCompletedProp = resolveList.Cast <IProperty>().First(p => p.GetParameters().Length == 0 && p.IsPublic); } if (isCompletedProp == null) { throw new ArgumentException("No valid IsCompleted property found"); } } var isCompletedMethod = isCompletedProp.GetGetMethod(); IType type; if (IsCustomTaskType(expression.ExpressionType)) { type = getResult.ReturnType; } else { type = expression.ExpressionType.ConstructedInfo == null ? TypeSystemServices.VoidType : expression.ExpressionType.ConstructedInfo.GenericArguments[0]; } // The awaiter temp facilitates EnC method remapping and thus have to be long-lived. // It transfers the awaiter objects from the old version of the MoveNext method to the new one. var awaiterType = getAwaiter.ReturnType; var awaiterTemp = CodeBuilder.DeclareTempLocal(_moveNext.Method, awaiterType); var getAwaiterInvocation = getAwaiter.IsExtension ? CodeBuilder.CreateMethodInvocation(getAwaiter, expression) : CodeBuilder.CreateMethodInvocation(expression, getAwaiter); var awaitIfIncomplete = new Block( // temp $awaiterTemp = <expr>.GetAwaiter(); new ExpressionStatement( CodeBuilder.CreateAssignment( CodeBuilder.CreateLocalReference(awaiterTemp), getAwaiterInvocation)), // if(!($awaiterTemp.IsCompleted)) { ... } new IfStatement( new UnaryExpression( UnaryOperatorType.LogicalNot, GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), GenerateAwaitForIncompleteTask(awaiterTemp), null)); TryStatementInfo currentTry = _tryStatementStack.Count > 0 ? _tryStatementStack.Peek() : null; if (currentTry != null) { ConvertTryStatement(currentTry); } var getResultCall = CodeBuilder.CreateMethodInvocation( CodeBuilder.CreateLocalReference(awaiterTemp), getResult); var nullAwaiter = CodeBuilder.CreateAssignment( CodeBuilder.CreateLocalReference(awaiterTemp), CodeBuilder.CreateDefaultInvocation(this.LexicalInfo, awaiterTemp.Type)); if (resultPlace != null && type != TypeSystemServices.VoidType) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp InternalLocal resultTemp = CodeBuilder.DeclareTempLocal(_moveNext.Method, type); return(new Block( awaitIfIncomplete, new ExpressionStatement( CodeBuilder.CreateAssignment(CodeBuilder.CreateLocalReference(resultTemp), getResultCall)), new ExpressionStatement(nullAwaiter), new ExpressionStatement( CodeBuilder.CreateAssignment(resultPlace, CodeBuilder.CreateLocalReference(resultTemp))))); } // $awaiterTemp.GetResult(); // $awaiterTemp = null; return(new Block( awaitIfIncomplete, new ExpressionStatement(getResultCall), new ExpressionStatement(nullAwaiter))); }