public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) { bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data); UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression; if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) { PointerReferenceExpression pre = new PointerReferenceExpression(); pre.Target = uoe.Expression.Detach(); pre.MemberNameToken = (Identifier)memberReferenceExpression.MemberNameToken.Clone(); memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments); pre.CopyAnnotationsFrom(uoe); pre.CopyAnnotationsFrom(memberReferenceExpression); pre.AddAnnotation(memberReferenceExpression.GetAllRecursiveILRanges()); memberReferenceExpression.ReplaceWith(pre); } return(result); }
public override object VisitBlockStatement(BlockStatement blockStatement, object data) { int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count; base.VisitBlockStatement(blockStatement, data); foreach (ExpressionStatement stmt in blockStatement.Statements.OfType <ExpressionStatement>().ToArray()) { Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); if (!displayClassAssignmentMatch.Success) { continue; } ILVariable variable = displayClassAssignmentMatch.Get <AstNode>("variable").Single().Annotation <ILVariable>(); if (variable == null) { continue; } TypeDef type = variable.Type.ToTypeDefOrRef().ResolveWithinSameModule(); if (!IsPotentialClosure(context, type)) { continue; } if (displayClassAssignmentMatch.Get <AstType>("type").Single().Annotation <ITypeDefOrRef>().ResolveWithinSameModule() != type) { continue; } // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses: bool ok = true; foreach (var identExpr in blockStatement.Descendants.OfType <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation <IField>() != null && identExpr.Parent.Annotation <IField>().IsField)) { ok = false; } } } if (!ok) { continue; } Dictionary <IField, AstNode> dict = new Dictionary <IField, AstNode>(); // Delete the variable declaration statement: VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name); if (displayClassVarDecl != null) { displayClassVarDecl.Remove(); //TODO: Save ILRanges } // Delete the assignment statement: AstNode cur = stmt.NextSibling; stmt.Remove(); //TODO: Save ILRanges // Delete any following statements as long as they assign parameters to the display class BlockStatement rootBlock = blockStatement.Ancestors.OfType <BlockStatement>().LastOrDefault() ?? blockStatement; List <ILVariable> parameterOccurrances = rootBlock.Descendants.OfType <IdentifierExpression>() .Select(n => n.Annotation <ILVariable>()).Where(p => p != null && p.IsParameter).ToList(); AstNode next; for (; cur != null; cur = next) { next = cur.NextSibling; // Test for the pattern: // "variableName.MemberName = right;" ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( new AssignmentExpression( new NamedNode("left", new MemberReferenceExpression { Target = IdentifierExpression.Create(variable.Name, variable.IsParameter ? TextTokenType.Parameter : TextTokenType.Local), MemberName = Pattern.AnyString }), new AnyNode("right") ) ); Match m = closureFieldAssignmentPattern.Match(cur); if (m.Success) { FieldDef fieldDef = m.Get <MemberReferenceExpression>("left").Single().Annotation <IField>().ResolveFieldWithinSameModule(); AstNode right = m.Get <AstNode>("right").Single(); bool isParameter = false; bool isDisplayClassParentPointerAssignment = false; if (right is ThisReferenceExpression) { isParameter = true; } else if (right is IdentifierExpression) { // handle parameters only if the whole method contains no other occurrence except for 'right' ILVariable v = right.Annotation <ILVariable>(); isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1; if (!isParameter && IsPotentialClosure(context, v.Type.ToTypeDefOrRef().ResolveWithinSameModule())) { // parent display class within the same method // (closure2.localsX = closure1;) isDisplayClassParentPointerAssignment = true; } } else if (right is MemberReferenceExpression) { // copy of parent display class reference from an outer lambda // closure2.localsX = this.localsY MemberReferenceExpression mre = m.Get <MemberReferenceExpression>("right").Single(); do { // descend into the targets of the mre as long as the field types are closures FieldDef fieldDef2 = mre.Annotation <FieldDef>().ResolveFieldWithinSameModule(); if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ToTypeDefOrRef().ResolveWithinSameModule())) { break; } // if we finally get to a this reference, it's copying a display class parent pointer if (mre.Target is ThisReferenceExpression) { isDisplayClassParentPointerAssignment = true; } mre = mre.Target as MemberReferenceExpression; } while (mre != null); } if (isParameter || isDisplayClassParentPointerAssignment) { if (fieldDef != null) { dict[fieldDef] = right; } cur.Remove(); //TODO: Save ILRanges } else { break; } } else { break; } } // Now create variables for all fields of the display class (except for those that we already handled as parameters) List <Tuple <AstType, ILVariable> > variablesToDeclare = new List <Tuple <AstType, ILVariable> >(); foreach (FieldDef field in type.Fields) { if (field.IsStatic) { continue; // skip static fields } if (dict.ContainsKey(field)) // skip field if it already was handled as parameter { continue; } string capturedVariableName = field.Name; if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10) { capturedVariableName = capturedVariableName.Substring(10); } EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); currentlyUsedVariableNames.Add(capturedVariableName); ILVariable ilVar = new ILVariable { IsGenerated = true, Name = capturedVariableName, Type = field.FieldType, }; variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); dict[field] = IdentifierExpression.Create(capturedVariableName, TextTokenType.Local).WithAnnotation(ilVar); } // Now figure out where the closure was accessed and use the simpler replacement expression there: foreach (var identExpr in blockStatement.Descendants.OfType <IdentifierExpression>()) { if (identExpr.Identifier == variable.Name) { MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; AstNode replacement; var fieldDef = mre.Annotation <IField>().ResolveFieldWithinSameModule(); if (fieldDef != null && dict.TryGetValue(fieldDef, out replacement)) { var newReplacement = replacement.Clone(); newReplacement.AddAnnotation(mre.GetAllRecursiveILRanges()); mre.ReplaceWith(newReplacement); } } } // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { var newVarDecl = new VariableDeclarationStatement(tuple.Item2.IsParameter ? TextTokenType.Parameter : TextTokenType.Local, tuple.Item1, tuple.Item2.Name); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); return(null); }