> since you seemed to say in the beginning that you did not want to create 
> objects (if I read this correctly), why creating AutoCloseable instances here 
> would not pose a problem ?)

Best case scenario is that I get the same performance and memory footprint of 
the original code (included below).  So an AST transform is what I look to in 
order to type less code but get the same runtime outcome.  A macro method must 
replace an expression with another expression, so writing out a try statement 
is not possible AFAIK (unless it is embedded in a ClosureExpression or 
something like that).  However an AIC is a ConstructorCallExpression so that is 
possible with a macro method.  And I think the one extra plus going for the AIC 
AutoCloseable is it could be reused anywhere an AutoCloseable is accepted.

If there is something simpler/groovier I am interested to hear about it.  The 
suggestion of converting Foo into an AutoCloseable significantly changes the 
design/structure.  I'm more looking to rewrite the contents of the bar() method 
only.


class Foo {
  private state
  def bar() {
    def temp = state
    state = newState
    try {
      baz()
    } finally {
      state = temp
    }
  }
  def baz() {
    // make use of state; does not require previous values
  }
}




In any case, I did manage to prototype it out and it works.  Here it is in case 
anyone wanted to have a look.


class Foo {
  def bar() {
    println field
    try (def x = auto(field, 'new')) { // needed to provide "def x =" because 
parser validates for this and macro transform runs after that
      baz()
    }
    println field
  }
  def baz() {
    println field
  }

  private field = 'old'
}

new Foo().bar() // should print "old", "new", "old"





import static org.codehaus.groovy.ast.ClassHelper.*
import static org.codehaus.groovy.ast.tools.GeneralUtils.*

import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.macro.runtime.Macro
import org.codehaus.groovy.macro.runtime.MacroContext

import groovy.transform.AutoFinal

@AutoFinal
class MoreMacroMethods {

  @Macro
  static Expression auto(MacroContext context, VariableExpression oldValue, 
Expression newValue = oldValue) {
    if (isImplicitThis(context)) {
      ClassNode type = newAutoCloseable(context)

      FieldNode field = type.addField('temp', 0x12, OBJECT_TYPE, oldValue)

      type.addObjectInitializerStatements(block(assignS(varX(oldValue.name), 
newValue)))

      type.addMethod('close', 0x11, VOID_TYPE, Parameter.EMPTY_ARRAY, new 
ClassNode[] {make(Exception.class)}, null).tap {
        addAnnotation(new AnnotationNode(make(Override.class)))
        setCode(assignS(varX(oldValue.name), fieldX(field)))
        setSourcePosition(context.call)
      }

      return new ConstructorCallExpression(type, 
ArgumentListExpression.EMPTY_ARGUMENTS).tap {
        usingAnonymousInnerClass = true
      }
    }
  }

  private static ClassNode newAutoCloseable(MacroContext context) {
    def (ClassNode enclosingClass, MethodNode enclosingMethod) = 
findEnclosing(context)
    def name = enclosingClass.name + '$' + (anonymousClassCount(enclosingClass) 
+ 1)
    def type = new InnerClassNode(enclosingClass, name, 1, AUTOCLOSEABLE_TYPE)
    type.anonymous = true; type.enclosingMethod = enclosingMethod

    context.compilationUnit.addNewPhaseOperation({ 
org.codehaus.groovy.control.SourceUnit unit ->
      if (unit.is(context.sourceUnit)) {
        unit.AST.addClass(type)
      }
    } as org.codehaus.groovy.control.CompilationUnit.ISourceUnitOperation, 3)

    return type
  }

  private static int anonymousClassCount(ClassNode node) {
    int count = 0
    for (Iterator<InnerClassNode> it = node.getInnerClasses(); it.hasNext();) {
      InnerClassNode innerClass = it.next()
      if (innerClass.isAnonymous()) {
        count += 1
      }
    }
    return count
  }

  private static Tuple2<ClassNode, MethodNode> findEnclosing(MacroContext 
context) {
    Tuple.tuple(context.sourceUnit.AST.classes[1], 
context.sourceUnit.AST.classes[1].methods[0]) // TODO: search 
context.sourceUnit.AST for enclosing type and method of context.call
  }

  private static boolean isImplicitThis(MacroContext context) {
    context.call.with {
      objectExpression instanceof VariableExpression &&
      objectExpression.isThisExpression() &&
      isImplicitThis()
    }
  }
}

Reply via email to