diff options
Diffstat (limited to 'src/utils/lombok')
-rw-r--r-- | src/utils/lombok/javac/CommentCatcher.java | 31 | ||||
-rw-r--r-- | src/utils/lombok/javac/Javac.java | 337 |
2 files changed, 302 insertions, 66 deletions
diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index 565a166d..8d1e71c0 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Project Lombok Authors. + * Copyright (C) 2011-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,9 +21,12 @@ */ package lombok.javac; +import java.lang.reflect.InvocationTargetException; import java.util.Map; import java.util.WeakHashMap; +import lombok.Lombok; + import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; @@ -62,31 +65,33 @@ public class CommentCatcher { private static void registerCommentsCollectingScannerFactory(Context context) { try { + Class<?> scannerFactory; if (Javac.getJavaCompilerVersion() <= 6) { - Class.forName("lombok.javac.java6.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context); + scannerFactory = Class.forName("lombok.javac.java6.CommentCollectingScannerFactory"); } else { - Class.forName("lombok.javac.java7.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context); + scannerFactory = Class.forName("lombok.javac.java7.CommentCollectingScannerFactory"); } + scannerFactory.getMethod("preRegister", Context.class).invoke(null, context); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException)e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); } } private static void setInCompiler(JavaCompiler compiler, Context context, Map<JCCompilationUnit, List<CommentInfo>> commentsMap) { - try { + Class<?> parserFactory; if (Javac.getJavaCompilerVersion() <= 6) { - Class<?> parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory"); - parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap); + parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory"); } else { - Class<?> parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory"); - parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap); + parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory"); } + parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException)e; - throw new RuntimeException(e); + throw Lombok.sneakyThrow(e); } } - } diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 4f316d9f..dfa51e00 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -25,19 +25,31 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; 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 com.sun.tools.javac.code.TypeTags; +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; @@ -47,31 +59,39 @@ import com.sun.tools.javac.util.Name; */ public class Javac { private Javac() { - //prevent instantiation + // prevent instantiation } /** 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 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) return minor; + 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. + * Checks if the given expression (that really ought to refer to a type + * expression) represents a primitive type. */ public static boolean isPrimitive(JCExpression ref) { String typeName = ref.toString(); @@ -79,15 +99,16 @@ public class Javac { } /** - * Turns an expression into a guessed intended literal. Only works for literals, as you can imagine. + * Turns an expression into a guessed intended literal. Only works for + * literals, as you can imagine. * * Will for example turn a TrueLiteral into 'Boolean.valueOf(true)'. */ public static Object calculateGuess(JCExpression expr) { if (expr instanceof JCLiteral) { - JCLiteral lit = (JCLiteral)expr; + JCLiteral lit = (JCLiteral) expr; if (lit.getKind() == com.sun.source.tree.Tree.Kind.BOOLEAN_LITERAL) { - return ((Number)lit.value).intValue() == 0 ? false : true; + return ((Number) lit.value).intValue() == 0 ? false : true; } return lit.value; } else if (expr instanceof JCIdent || expr instanceof JCFieldAccess) { @@ -98,49 +119,231 @@ public class Javac { if (idx > -1) x = x.substring(idx + 1); } return x; - } else return null; - } - - public static final int CTC_BOOLEAN = getCtcInt(TypeTags.class, "BOOLEAN"); - public static final int CTC_INT = getCtcInt(TypeTags.class, "INT"); - public static final int CTC_DOUBLE = getCtcInt(TypeTags.class, "DOUBLE"); - public static final int CTC_FLOAT = getCtcInt(TypeTags.class, "FLOAT"); - public static final int CTC_SHORT = getCtcInt(TypeTags.class, "SHORT"); - public static final int CTC_BYTE = getCtcInt(TypeTags.class, "BYTE"); - public static final int CTC_LONG = getCtcInt(TypeTags.class, "LONG"); - public static final int CTC_CHAR = getCtcInt(TypeTags.class, "CHAR"); - public static final int CTC_VOID = getCtcInt(TypeTags.class, "VOID"); - public static final int CTC_NONE = getCtcInt(TypeTags.class, "NONE"); - - public static final int CTC_NOT_EQUAL = getCtcInt(JCTree.class, "NE"); - public static final int CTC_NOT = getCtcInt(JCTree.class, "NOT"); - public static final int CTC_BITXOR = getCtcInt(JCTree.class, "BITXOR"); - public static final int CTC_UNSIGNED_SHIFT_RIGHT = getCtcInt(JCTree.class, "USR"); - public static final int CTC_MUL = getCtcInt(JCTree.class, "MUL"); - public static final int CTC_PLUS = getCtcInt(JCTree.class, "PLUS"); - public static final int CTC_BOT = getCtcInt(TypeTags.class, "BOT"); - public static final int CTC_EQUAL = getCtcInt(JCTree.class, "EQ"); + } else + return null; + } + + public static final Object CTC_BOOLEAN = getTypeTag("BOOLEAN"); + public static final Object CTC_INT = getTypeTag("INT"); + public static final Object CTC_DOUBLE = getTypeTag("DOUBLE"); + public static final Object CTC_FLOAT = getTypeTag("FLOAT"); + public static final Object CTC_SHORT = getTypeTag("SHORT"); + public static final Object CTC_BYTE = getTypeTag("BYTE"); + public static final Object CTC_LONG = getTypeTag("LONG"); + public static final Object CTC_CHAR = getTypeTag("CHAR"); + public static final Object CTC_VOID = getTypeTag("VOID"); + public static final Object CTC_NONE = getTypeTag("NONE"); + public static final Object CTC_BOT = getTypeTag("BOT"); + public static final Object CTC_CLASS = getTypeTag("CLASS"); + + public static final Object CTC_NOT_EQUAL = getTreeTag("NE"); + public static final Object CTC_NOT = getTreeTag("NOT"); + public static final Object CTC_BITXOR = getTreeTag("BITXOR"); + public static final Object CTC_UNSIGNED_SHIFT_RIGHT = getTreeTag("USR"); + public static final Object CTC_MUL = getTreeTag("MUL"); + public static final Object CTC_PLUS = getTreeTag("PLUS"); + public static final Object CTC_EQUAL = getTreeTag("EQ"); + + public static boolean compareCTC(Object ctc1, Object 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 a compile time constant of type int from the specified class location. + * Retrieves the provided TypeTag value, in a compiler version independent manner. * - * 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) + * 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 ctcLocation location of the compile time constant - * @param identifier the name of the field of the compile time 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 int getCtcInt(Class<?> ctcLocation, String identifier) { + public static Object getTypeTag(String identifier) { + 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) { + 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 { - return (Integer)ctcLocation.getField(identifier).get(null); + 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); + 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; + } + + public static Object getTreeTypeTag(JCLiteral tree) { + return tree.typetag; } - private static final Field JCTREE_TAG; + 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 { + return (JCExpression) createIdent.invoke(maker, ctc); + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } + } + + public static JCLiteral makeLiteral(TreeMaker maker, Object ctc, Object argument) { + try { + return (JCLiteral) createLiteral.invoke(maker, ctc, argument); + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } + } + + public static JCUnary makeUnary(TreeMaker maker, Object ctc, JCExpression argument) { + try { + return (JCUnary) createUnary.invoke(maker, ctc, argument); + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } + } + + public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression lhsArgument, JCExpression rhsArgument) { + try { + return (JCBinary) createBinary.invoke(maker, ctc, lhsArgument, rhsArgument); + } catch (IllegalAccessException 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; @@ -149,6 +352,18 @@ public class Javac { } 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"); @@ -156,36 +371,52 @@ public class Javac { JCTREE_GETTAG = m; } - public static int getTag(JCTree node) { + public static Object getTag(JCTree node) { if (JCTREE_GETTAG != null) { try { - return (Integer) JCTREE_GETTAG.invoke(node); + return JCTREE_GETTAG.invoke(node); } catch (Exception e) {} } try { - return (Integer) JCTREE_TAG.get(node); + return JCTREE_TAG.get(node); } catch (Exception e) { throw new IllegalStateException("Can't get node tag"); } } - private static Method method; + 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 (method == null) try { - method = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCExpression.class, List.class, List.class); + 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 (method == null) try { - method = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCTree.class, List.class, List.class); + 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 (method == null) throw new IllegalStateException("Lombok bug #20130617-1310: ClassDef doesn't look like anything we thought it would look like."); - if (!Modifier.isPublic(method.getModifiers()) && !method.isAccessible()) { - method.setAccessible(true); + 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) method.invoke(maker, mods, name, typarams, extending, implementing, defs); + return (JCClassDecl) classDef.invoke(maker, mods, name, typarams, extending, implementing, defs); } catch (InvocationTargetException e) { throw sneakyThrow(e.getCause()); } catch (IllegalAccessException e) { |