This is substantially lower-level than is probably warranted, but what
you're looking for could be implemented by pushing the values onto the
stack immediately at the entry point.

On Tue, Nov 24, 2020, 15:36 Milles, Eric (TR Technology) <
eric.mil...@thomsonreuters.com> wrote:

> > 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