编译期修改 AST,实现对类的增强

TreeMaker

TreeMaker 用于创建一系列的语法树节点,创建 JCTree 不能直接使用 new 来创建,Java 为我们提供了一个工具类 TreeMaker,它会在创建时为我们创建的 JCTree 对象设置 pos 字段,所以必须使用上下文相关的 TreeMaker 对象来创建语法树节点。

TreeMaker 常用方法

Modifiers

用于创建访问标志语法树节点(JCModifiers)

public JCModifiers Modifiers(long flags, List<JCAnnotation> annotations) {
    JCModifiers tree = new JCModifiers(flags, annotations);
    boolean noFlags = (flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0;
    tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
    return tree;
}

public JCModifiers Modifiers(long flags) {
    return Modifiers(flags, List.<JCAnnotation>nil());
}

入参的flags使用com.sun.tools.javac.code.Flags中的常量值,例如:

private static final
Flags.PRIVATE | Flags.STATIC | Flags.FINALs

ClassDef

用于创建类定义语法树节点(JCClassDecl)

public JCClassDecl ClassDef(JCModifiers mods,
                            Name name,
                            List<JCTypeParameter> typarams,
                            JCExpression extending,
                            List<JCExpression> implementing,
                            List<JCTree> defs) {
    JCClassDecl tree = new JCClassDecl(mods,
                                 name,
                                 typarams,
                                 extending,
                                 implementing,
                                 defs,
                                 null);
    tree.pos = pos;
    return tree;
}

MethodDef

用于创建方法定义语法树节点(JCMethodDecl)

public JCMethodDecl MethodDef(JCModifiers mods,
                           Name name,
                           JCExpression restype,
                           List<JCTypeParameter> typarams,
                           List<JCVariableDecl> params,
                           List<JCExpression> thrown,
                           JCBlock body,
                           JCExpression defaultValue) {
    return MethodDef(
            mods, name, restype, typarams, null, params,
            thrown, body, defaultValue);
}

public JCMethodDecl MethodDef(JCModifiers mods,
                           Name name,
                           JCExpression restype,
                           List<JCTypeParameter> typarams,
                           JCVariableDecl recvparam,
                           List<JCVariableDecl> params,
                           List<JCExpression> thrown,
                           JCBlock body,
                           JCExpression defaultValue) {
    JCMethodDecl tree = new JCMethodDecl(mods,
                                   name,
                                   restype,
                                   typarams,
                                   recvparam,
                                   params,
                                   thrown,
                                   body,
                                   defaultValue,
                                   null);
    tree.pos = pos;
    return tree;
}

public JCMethodDecl MethodDef(MethodSymbol m, JCBlock body) {
    return MethodDef(m, m.type, body);
}

public JCMethodDecl MethodDef(MethodSymbol m, Type mtype, JCBlock body) {
    return (JCMethodDecl)
        new JCMethodDecl(
            Modifiers(m.flags(), Annotations(m.getRawAttributes())),
            m.name,
            Type(mtype.getReturnType()),
            TypeParams(mtype.getTypeArguments()),
            null, // receiver type
            Params(mtype.getParameterTypes(), m),
            Types(mtype.getThrownTypes()),
            body,
            null,
            m).setPos(pos).setType(mtype);
}

VarDef

用于创建字段/变量定义语法树节点(JCVariableDecl)

public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init) {
    JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
    tree.pos = pos;
    return tree;
}

public JCVariableDecl VarDef(VarSymbol v, JCExpression init) {
    return (JCVariableDecl)
        new JCVariableDecl(
            Modifiers(v.flags(), Annotations(v.getRawAttributes())),
            v.name,
            Type(v.type),
            init,
            v).setPos(pos).setType(v.type);
}

Ident

用于创建标识符语法树节点(JCIdent)

public JCExpression Ident(JCVariableDecl param) {
    return Ident(param.sym);
}

public JCIdent Ident(Name name) {
    JCIdent tree = new JCIdent(name, null);
    tree.pos = pos;
    return tree;
}

public JCIdent Ident(Symbol sym) {
    return (JCIdent)new JCIdent((sym.name != names.empty)
                            ? sym.name
                            : sym.flatName(), sym)
        .setPos(pos)
        .setType(sym.type);
}

Return

用于创建return语句语法树节点(JCReturn)

public JCReturn Return(JCExpression expr) {
    JCReturn tree = new JCReturn(expr);
    tree.pos = pos;
    return tree;
}

Select

用于创建域访问/方法访问(这里的方法访问只是取到名字,方法的调用需要用TreeMaker.Apply)语法树节点(JCFieldAccess)

public JCFieldAccess Select(JCExpression selected, Name selector) {
    JCFieldAccess tree = new JCFieldAccess(selected, selector, null);
    tree.pos = pos;
    return tree;
}

public JCExpression Select(JCExpression base, Symbol sym) {
    return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);
}

NewClass

用于创建new XXX()语法树节点(JCNewClass)

public JCNewClass NewClass(JCExpression encl,
                         List<JCExpression> typeargs,
                         JCExpression clazz,
                         List<JCExpression> args,
                         JCClassDecl def) {
    JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);
    tree.pos = pos;
    return tree;
}

Apply

用于创建方法调用语法树节点(JCMethodInvocation)

public JCMethodInvocation Apply(List<JCExpression> typeargs,
                   JCExpression fn,
                   List<JCExpression> args) {
    JCMethodInvocation tree = new JCMethodInvocation(typeargs, fn, args);
    tree.pos = pos;
    return tree;
}

Assign

用于创建赋值语句语法树节点(JCAssign)

public JCAssign Assign(JCExpression lhs, JCExpression rhs) {
    JCAssign tree = new JCAssign(lhs, rhs);
    tree.pos = pos;
    return tree;
}

Exec

用于创建可执行语句语法树节点(JCExpressionStatement)

public JCExpressionStatement Exec(JCExpression expr) {
    JCExpressionStatement tree = new JCExpressionStatement(expr);
    tree.pos = pos;
    return tree;
}

TreeMaker.Apply、TreeMaker.Assign 就需要外面包一层 TreeMaker.Exec 来获得一个 JCExpressionStatement

Block

用于创建组合语句语法树节点(JCBlock)

public JCBlock Block(long flags, List<JCStatement> stats) {
    JCBlock tree = new JCBlock(flags, stats);
    tree.pos = pos;
    return tree;
}

其他常用类

com.sun.tools.javac.util.List

JSR-269 API 中会涉及到一个 List,这个 List 不是 java.util.List,它是 com.sun.tools.javac.util.List。List 包含两个字段,head 和 tail,其中 head 只是一个节点,而 tail 是一个 List

public class List<A> extends AbstractCollection<A> implements java.util.List<A> {
    public A head;
    public List<A> tail;
    private static final List<?> EMPTY_LIST = new List<Object>((Object)null, (List)null) {
        public List<Object> setTail(List<Object> var1) {
            throw new UnsupportedOperationException();
        }

        public boolean isEmpty() {
            return true;
        }
    };

    List(A head, List<A> tail) {
        this.tail = tail;
        this.head = head;
    }

    public static <A> List<A> nil() {
        return EMPTY_LIST;
    }

    public List<A> prepend(A var1) {
        return new List(var1, this);
    }

    public List<A> append(A var1) {
        return of(var1).prependList(this);
    }

    public static <A> List<A> of(A var0) {
        return new List(var0, nil());
    }

    public static <A> List<A> of(A var0, A var1) {
        return new List(var0, of(var1));
    }

    public static <A> List<A> of(A var0, A var1, A var2) {
        return new List(var0, of(var1, var2));
    }

    public static <A> List<A> of(A var0, A var1, A var2, A... var3) {
        return new List(var0, new List(var1, new List(var2, from(var3))));
    }

    ...
}

com.sun.tools.javac.util.ListBuffer

由于 com.sun.tools.javac.util.List 用起来不是很方便,而 ListBuffer 的行为与 java.util.List 的行为类似,并且提供了转换成 com.sun.tools.javac.util.List 的方法

ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();

//添加语句 " this.xxx = xxx; "
jcStatements.append(...);

//添加Builder模式中的返回语句 " return this; "
jcStatements.append(...);

List<JCTree.JCStatement> lst = jcStatements.toList();

参考

Java-JSR-269-插入式注解处理器