diff options
Diffstat (limited to 'src/utils/lombok/javac/Javac.java')
-rw-r--r-- | src/utils/lombok/javac/Javac.java | 367 |
1 files changed, 282 insertions, 85 deletions
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 9f2936a8..dfa51e00 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,19 +21,38 @@ */ package lombok.javac; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Objects; +import java.lang.reflect.Modifier; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.lang.model.type.NoType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeVisitor; + +import lombok.Lombok; + +import com.sun.tools.javac.code.Type; import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; /** * Container for static utility methods relevant to lombok's operation on javac. @@ -46,6 +65,30 @@ public class Javac { /** Matches any of the 8 primitive names, such as {@code boolean}. */ private static final Pattern PRIMITIVE_TYPE_NAME_PATTERN = Pattern.compile("^(boolean|byte|short|int|long|float|double|char)$"); + private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.(\\d{1,6}).*$"); + + private static final AtomicInteger compilerVersion = new AtomicInteger(-1); + + /** + * Returns the version of this java compiler, i.e. the JDK that it shipped in. For example, for javac v1.7, this returns {@code 7}. + */ + public static int getJavaCompilerVersion() { + int cv = compilerVersion.get(); + if (cv != -1) return cv; + Matcher m = VERSION_PARSER.matcher(JavaCompiler.version()); + if (m.matches()) { + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + if (major == 1) { + compilerVersion.set(minor); + return minor; + } + } + + compilerVersion.set(6); + return 6; + } + /** * Checks if the given expression (that really ought to refer to a type * expression) represents a primitive type. @@ -102,56 +145,53 @@ public class Javac { public static final Object CTC_EQUAL = getTreeTag("EQ"); public static boolean compareCTC(Object ctc1, Object ctc2) { - return Objects.equals(ctc1, ctc2); + return ctc1 == null ? ctc2 == null : ctc1.equals(ctc2); } + private static final ConcurrentMap<String, Object> TYPE_TAG_CACHE = new ConcurrentHashMap<String, Object>(); + private static final ConcurrentMap<String, Object> TREE_TAG_CACHE = new ConcurrentHashMap<String, Object>(); + + /** - * Retrieves the current type tag. The actual type object differs depending on the Compiler version - * - * For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code> - * for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code> + * Retrieves the provided TypeTag value, in a compiler version independent manner. * + * The actual type object differs depending on the Compiler version: + * <ul> + * <li>For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code> + * <li>for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code> + * </ul> * Solves the problem of compile time constant inlining, resulting in lombok * having the wrong value (javac compiler changes private api constants from * time to time). * - * @param identifier - * @return the ordinal value of the typetag constant + * @param identifier Identifier to turn into a TypeTag. + * @return the value of the typetag constant (either enum instance or an Integer object). */ public static Object getTypeTag(String identifier) { - try { - if (JavaCompiler.version().startsWith("1.8")) { - return Class.forName("com.sun.tools.javac.code.TypeTag").getField(identifier).get(null); - } else { - return Class.forName("com.sun.tools.javac.code.TypeTags").getField(identifier).get(null); - } - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); - } + return getFieldCached(TYPE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.code.TypeTag" : "com.sun.tools.javac.code.TypeTags", identifier); } public static Object getTreeTag(String identifier) { - try { - if (JavaCompiler.version().startsWith("1.8")) { - return Class.forName("com.sun.tools.javac.tree.JCTree$Tag").getField(identifier).get(null); - } else { - return Class.forName("com.sun.tools.javac.tree.JCTree").getField(identifier).get(null); - } + return getFieldCached(TREE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.tree.JCTree" : "com.sun.tools.javac.tree.JCTree$Tag", identifier); + } + + private static Object getFieldCached(ConcurrentMap<String, Object> cache, String className, String fieldName) { + Object value = cache.get(fieldName); + if (value != null) return value; + try { + value = Class.forName(className).getField(fieldName).get(null); } catch (NoSuchFieldException e) { - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); + } catch (ClassNotFoundException e) { + throw Lombok.sneakyThrow(e); } + + cache.putIfAbsent(fieldName, value); + return value; } - + public static Object getTreeTypeTag(JCPrimitiveTypeTree tree) { return tree.typetag; } @@ -159,82 +199,239 @@ public class Javac { public static Object getTreeTypeTag(JCLiteral tree) { return tree.typetag; } + + private static final Method createIdent, createLiteral, createUnary, createBinary; + static { + if (getJavaCompilerVersion() < 8) { + createIdent = getMethod(TreeMaker.class, "TypeIdent", int.class); + } else { + createIdent = getMethod(TreeMaker.class, "TypeIdent", "com.sun.tools.javac.code.TypeTag"); + } + createIdent.setAccessible(true); + + if (getJavaCompilerVersion() < 8) { + createLiteral = getMethod(TreeMaker.class, "Literal", int.class, Object.class); + } else { + createLiteral = getMethod(TreeMaker.class, "Literal", "com.sun.tools.javac.code.TypeTag", "java.lang.Object"); + } + createLiteral.setAccessible(true); + + if (getJavaCompilerVersion() < 8) { + createUnary = getMethod(TreeMaker.class, "Unary", int.class, JCExpression.class); + } else { + createUnary = getMethod(TreeMaker.class, "Unary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName()); + } + createUnary.setAccessible(true); + + if (getJavaCompilerVersion() < 8) { + createBinary = getMethod(TreeMaker.class, "Binary", Integer.TYPE, JCExpression.class, JCExpression.class); + } else { + createBinary = getMethod(TreeMaker.class, "Binary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName(), JCExpression.class.getName()); + } + createBinary.setAccessible(true); + } + + private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) { + try { + return clazz.getMethod(name, paramTypes); + } catch (NoSuchMethodException e) { + throw Lombok.sneakyThrow(e); + } + } + + private static Method getMethod(Class<?> clazz, String name, String... paramTypes) { + try { + Class<?>[] c = new Class[paramTypes.length]; + for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]); + return clazz.getMethod(name, c); + } catch (NoSuchMethodException e) { + throw Lombok.sneakyThrow(e); + } catch (ClassNotFoundException e) { + throw Lombok.sneakyThrow(e); + } + } + public static JCExpression makeTypeIdent(TreeMaker maker, Object ctc) { try { - Method createIdent; - if (JavaCompiler.version().startsWith("1.8")) { - createIdent = TreeMaker.class.getMethod("TypeIdent", Class.forName("com.sun.tools.javac.code.TypeTag")); - } else { - createIdent = TreeMaker.class.getMethod("TypeIdent", Integer.TYPE); - } return (JCExpression) createIdent.invoke(maker, ctc); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); } } public static JCLiteral makeLiteral(TreeMaker maker, Object ctc, Object argument) { try { - Method createLiteral; - if (JavaCompiler.version().startsWith("1.8")) { - createLiteral = TreeMaker.class.getMethod("Literal", Class.forName("com.sun.tools.javac.code.TypeTag"), Object.class); - } else { - createLiteral = TreeMaker.class.getMethod("Literal", Integer.TYPE, Object.class); - } return (JCLiteral) createLiteral.invoke(maker, ctc, argument); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); } } public static JCUnary makeUnary(TreeMaker maker, Object ctc, JCExpression argument) { try { - Method createUnary; - if (JavaCompiler.version().startsWith("1.8")) { - createUnary = TreeMaker.class.getMethod("Unary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class); - } else { - createUnary = TreeMaker.class.getMethod("Unary", Integer.TYPE, JCExpression.class); - } return (JCUnary) createUnary.invoke(maker, ctc, argument); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); } } - public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression rhsArgument, JCExpression lhsArgument) { + public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression lhsArgument, JCExpression rhsArgument) { try { - Method createUnary; - if (JavaCompiler.version().startsWith("1.8")) { - createUnary = TreeMaker.class.getMethod("Binary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class, JCExpression.class); - } else { - createUnary = TreeMaker.class.getMethod("Binary", Integer.TYPE, JCExpression.class, JCExpression.class); - } - return (JCBinary) createUnary.invoke(maker, ctc, rhsArgument, lhsArgument); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); + return (JCBinary) createBinary.invoke(maker, ctc, lhsArgument, rhsArgument); } catch (IllegalAccessException e) { - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } + } + + private static final Class<?> JC_VOID_TYPE, JC_NO_TYPE; + + static { + Class<?> c = null; + try { + c = Class.forName("com.sun.tools.javac.code.Type$JCVoidType"); + } catch (Exception ignore) {} + JC_VOID_TYPE = c; + c = null; + try { + c = Class.forName("com.sun.tools.javac.code.Type$JCNoType"); + } catch (Exception ignore) {} + JC_NO_TYPE = c; + } + + public static Type createVoidType(TreeMaker maker, Object tag) { + if (Javac.getJavaCompilerVersion() < 8) { + return new JCNoType(((Integer) tag).intValue()); + } else { + try { + if (compareCTC(tag, CTC_VOID)) { + return (Type) JC_VOID_TYPE.newInstance(); + } else { + return (Type) JC_NO_TYPE.newInstance(); + } + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InstantiationException e) { + throw Lombok.sneakyThrow(e); + } + } + } + + private static class JCNoType extends Type implements NoType { + public JCNoType(int tag) { + super(tag, null); + } + + @Override + public TypeKind getKind() { + if (Javac.compareCTC(tag, CTC_VOID)) return TypeKind.VOID; + if (Javac.compareCTC(tag, CTC_NONE)) return TypeKind.NONE; + throw new AssertionError("Unexpected tag: " + tag); + } + + @Override + public <R, P> R accept(TypeVisitor<R, P> v, P p) { + return v.visitNoType(this, p); + } + } + + private static final Field JCTREE_TAG, JCLITERAL_TYPETAG, JCPRIMITIVETYPETREE_TYPETAG; + private static final Method JCTREE_GETTAG; + static { + Field f = null; + try { + f = JCTree.class.getDeclaredField("tag"); + } catch (NoSuchFieldException e) {} + JCTREE_TAG = f; + + f = null; + try { + f = JCLiteral.class.getDeclaredField("typetag"); + } catch (NoSuchFieldException e) {} + JCLITERAL_TYPETAG = f; + + f = null; + try { + f = JCPrimitiveTypeTree.class.getDeclaredField("typetag"); + } catch (NoSuchFieldException e) {} + JCPRIMITIVETYPETREE_TYPETAG = f; + + Method m = null; + try { + m = JCTree.class.getDeclaredMethod("getTag"); + } catch (NoSuchMethodException e) {} + JCTREE_GETTAG = m; + } + + public static Object getTag(JCTree node) { + if (JCTREE_GETTAG != null) { + try { + return JCTREE_GETTAG.invoke(node); + } catch (Exception e) {} + } + try { + return JCTREE_TAG.get(node); } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException) e; - throw new RuntimeException(e); + throw new IllegalStateException("Can't get node tag"); } } - + public static Object getTypeTag(JCLiteral node) { + try { + return JCLITERAL_TYPETAG.get(node); + } catch (Exception e) { + throw new IllegalStateException("Can't get JCLiteral typetag"); + } + } + + public static Object getTypeTag(JCPrimitiveTypeTree node) { + try { + return JCPRIMITIVETYPETREE_TYPETAG.get(node); + } catch (Exception e) { + throw new IllegalStateException("Can't get JCPrimitiveTypeTree typetag"); + } + } + + private static Method classDef; + + public static JCClassDecl ClassDef(TreeMaker maker, JCModifiers mods, Name name, List<JCTypeParameter> typarams, JCExpression extending, List<JCExpression> implementing, List<JCTree> defs) { + if (classDef == null) try { + classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCExpression.class, List.class, List.class); + } catch (NoSuchMethodException ignore) {} + if (classDef == null) try { + classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCTree.class, List.class, List.class); + } catch (NoSuchMethodException ignore) {} + + if (classDef == null) throw new IllegalStateException("Lombok bug #20130617-1310: ClassDef doesn't look like anything we thought it would look like."); + if (!Modifier.isPublic(classDef.getModifiers()) && !classDef.isAccessible()) { + classDef.setAccessible(true); + } + + try { + return (JCClassDecl) classDef.invoke(maker, mods, name, typarams, extending, implementing, defs); + } catch (InvocationTargetException e) { + throw sneakyThrow(e.getCause()); + } catch (IllegalAccessException e) { + throw sneakyThrow(e.getCause()); + } + } + + private static RuntimeException sneakyThrow(Throwable t) { + if (t == null) throw new NullPointerException("t"); + Javac.<RuntimeException>sneakyThrow0(t); + return null; + } + + @SuppressWarnings("unchecked") + private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + throw (T)t; + } } |