From 07e921d2b19a660df28e03ee1ed1d0315d5e3458 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Sat, 3 Oct 2020 23:52:15 +0200 Subject: [javac] Added/improved support for the 'receiver parameter' feature --- src/utils/lombok/javac/JavacTreeMaker.java | 127 ++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 13 deletions(-) (limited to 'src/utils') diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java index 15a11151..30d71606 100644 --- a/src/utils/lombok/javac/JavacTreeMaker.java +++ b/src/utils/lombok/javac/JavacTreeMaker.java @@ -24,6 +24,7 @@ package lombok.javac; 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; @@ -106,7 +107,19 @@ public class JavacTreeMaker { return this; } - private static class MethodId { + private static final class FieldId { + private final Class owner; + private final String name; + private final Class fieldType; + + FieldId(Class owner, String name, Class fieldType) { + this.owner = owner; + this.name = name; + this.fieldType = fieldType; + } + } + + private static final class MethodId { private final Class owner; private final String name; private final Class returnType; @@ -332,9 +345,79 @@ public class JavacTreeMaker { throw new InternalError("Not found: " + name); } - private static final Object METHOD_NOT_FOUND = new Object[0]; - private static final Object METHOD_MULTIPLE_FOUND = new Object[0]; + static FieldId FieldId(Class owner, String name, Class fieldType) { + return new FieldId(owner, name, fieldType); + } + + private static final ConcurrentHashMap, Object> FIELD_CACHE = new ConcurrentHashMap, Object>(); + + private static boolean has(FieldId f) { + Object field = FIELD_CACHE.get(f); + if (field == REFLECTIVE_ITEM_NOT_FOUND) return false; + if (field instanceof Field) return true; + + try { + return getFromCache(f) != REFLECTIVE_ITEM_NOT_FOUND; + } catch (IllegalStateException e) { + return false; + } + } + + private static J get(Object owner, FieldId f) { + Field field = getFromCache(f); + try { + return f.fieldType.cast(field.get(owner)); + } catch (IllegalAccessException e) { + throw Javac.sneakyThrow(e); + } + } + + private static void set(Object owner, FieldId f, J val) { + Field field = getFromCache(f); + try { + field.set(owner, val); + } catch (IllegalAccessException e) { + throw Javac.sneakyThrow(e); + } catch (IllegalArgumentException e) { + System.err.println("Type mismatch for: " + field); + throw e; + } + } + + private static Field getFromCache(FieldId f) { + Object s = FIELD_CACHE.get(f); + if (s == null) s = addToCache(f); + if (s == REFLECTIVE_ITEM_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for field: " + f); + return (Field) s; + } + + private static Object addToCache(FieldId f) { + for (Field field : f.owner.getDeclaredFields()) { + if (f.name.equals(field.getName())) { + if (!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); + return FIELD_CACHE.putIfAbsent(f, field); + } + } + + return FIELD_CACHE.putIfAbsent(f, REFLECTIVE_ITEM_NOT_FOUND); + } + + private static final Object REFLECTIVE_ITEM_NOT_FOUND = new Object[0]; + private static final Object REFLECTIVE_ITEM_MULTIPLE_FOUND = new Object[0]; private static final ConcurrentHashMap, Object> METHOD_CACHE = new ConcurrentHashMap, Object>(); + + private boolean has(MethodId m) { + Object method = METHOD_CACHE.get(m); + if (method == REFLECTIVE_ITEM_NOT_FOUND) return false; + if (method instanceof Method) return true; + + try { + return getFromCache(m) != REFLECTIVE_ITEM_NOT_FOUND; + } catch (IllegalStateException e) { + return false; + } + } + private J invoke(MethodId m, Object... args) { return invokeAny(tm, m, args); } @@ -351,8 +434,8 @@ public class JavacTreeMaker { } catch (IllegalAccessException e) { throw Javac.sneakyThrow(e); } catch (IllegalArgumentException e) { - System.err.println(method); - throw Javac.sneakyThrow(e); + System.err.println("Type mismatch for: " + method); + throw e; } } @@ -366,8 +449,8 @@ public class JavacTreeMaker { private static Method getFromCache(MethodId m) { Object s = METHOD_CACHE.get(m); if (s == null) s = addToCache(m); - if (s == METHOD_MULTIPLE_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: multiple matches when looking for method: " + m); - if (s == METHOD_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for method: " + m); + if (s == REFLECTIVE_ITEM_MULTIPLE_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: multiple matches when looking for method: " + m); + if (s == REFLECTIVE_ITEM_NOT_FOUND) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for method: " + m); return (Method) s; } @@ -391,13 +474,13 @@ public class JavacTreeMaker { } if (found == null) found = method; else { - METHOD_CACHE.putIfAbsent(m, METHOD_MULTIPLE_FOUND); - return METHOD_MULTIPLE_FOUND; + METHOD_CACHE.putIfAbsent(m, REFLECTIVE_ITEM_MULTIPLE_FOUND); + return REFLECTIVE_ITEM_MULTIPLE_FOUND; } } if (found == null) { - METHOD_CACHE.putIfAbsent(m, METHOD_NOT_FOUND); - return METHOD_NOT_FOUND; + METHOD_CACHE.putIfAbsent(m, REFLECTIVE_ITEM_NOT_FOUND); + return REFLECTIVE_ITEM_NOT_FOUND; } Permit.setAccessible(found); Object marker = METHOD_CACHE.putIfAbsent(m, found); @@ -431,8 +514,12 @@ public class JavacTreeMaker { //javac versions: 8 private static final MethodId MethodDefWithRecvParam = MethodId("MethodDef", JCMethodDecl.class, JCModifiers.class, Name.class, JCExpression.class, List.class, JCVariableDecl.class, List.class, List.class, JCBlock.class, JCExpression.class); - public JCMethodDecl MethodDef(JCModifiers mods, Name name, JCExpression resType, List typarams, JCVariableDecl recvparam, List params, List thrown, JCBlock body, JCExpression defaultValue) { - return invoke(MethodDefWithRecvParam, mods, name, resType, recvparam, typarams, params, thrown, body, defaultValue); + public boolean hasMethodDefWithRecvParam() { + return has(MethodDefWithRecvParam); + } + + public JCMethodDecl MethodDefWithRecvParam(JCModifiers mods, Name name, JCExpression resType, List typarams, JCVariableDecl recvparam, List params, List thrown, JCBlock body, JCExpression defaultValue) { + return invoke(MethodDefWithRecvParam, mods, name, resType, typarams, recvparam, params, thrown, body, defaultValue); } //javac versions: 6-8 @@ -878,4 +965,18 @@ public class JavacTreeMaker { public JCExpression Type(Type type) { return invoke(Type, type); } + + private static final FieldId MethodDecl_recvParam = FieldId(JCMethodDecl.class, "recvparam", JCVariableDecl.class); + //javac versions: 8+ + public boolean hasReceiverParameter() { + return has(MethodDecl_recvParam); + } + + public JCVariableDecl getReceiverParameter(JCMethodDecl method) { + return get(method, MethodDecl_recvParam); + } + + public void setReceiverParameter(JCMethodDecl method, JCVariableDecl param) { + set(method, MethodDecl_recvParam, param); + } } \ No newline at end of file -- cgit