aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/lombok/Delegate.java2
-rw-r--r--src/core/lombok/eclipse/Eclipse.java2
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java2
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java11
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java223
5 files changed, 237 insertions, 3 deletions
diff --git a/src/core/lombok/Delegate.java b/src/core/lombok/Delegate.java
index 0bbaed0d..bbfd51df 100644
--- a/src/core/lombok/Delegate.java
+++ b/src/core/lombok/Delegate.java
@@ -22,5 +22,5 @@
package lombok;
public @interface Delegate {
- Class<?> value() default java.lang.Object.class;
+ Class<?>[] value() default {};
}
diff --git a/src/core/lombok/eclipse/Eclipse.java b/src/core/lombok/eclipse/Eclipse.java
index 8354215b..4101370c 100644
--- a/src/core/lombok/eclipse/Eclipse.java
+++ b/src/core/lombok/eclipse/Eclipse.java
@@ -299,7 +299,7 @@ public class Eclipse {
return ref;
}
- private static long pos(ASTNode node) {
+ public static long pos(ASTNode node) {
return ((long) node.sourceStart << 32) | (node.sourceEnd & 0xFFFFFFFFL);
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index f252793d..921d955e 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -506,7 +506,7 @@ public class EclipseHandlerUtil {
private static final char[] ALL = "all".toCharArray();
- private static Annotation[] createSuppressWarningsAll(ASTNode source, Annotation[] originalAnnotationArray) {
+ public static Annotation[] createSuppressWarningsAll(ASTNode source, Annotation[] originalAnnotationArray) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
long[] poss = new long[3];
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index 399ea357..c54ef604 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -257,9 +257,20 @@ public class EclipsePatcher extends Agent {
}
private static void patchEcjTransformers(ScriptManager sm, boolean ecj) {
+ patchDelegate(sm);
patchHandleVal(sm, ecj);
}
+ private static void patchDelegate(ScriptManager sm) {
+ final String TYPEDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration";
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(TYPEDECLARATION_SIG, "resolve", "void"))
+ .request(StackRequest.THIS)
+ .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "handleDelegateForType", "boolean", TYPEDECLARATION_SIG))
+ .build());
+ }
+
// Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out,
// which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy.
// Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors),
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
index e2a52929..e71caa3d 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
@@ -22,33 +22,60 @@
package lombok.eclipse.agent;
+import static lombok.eclipse.Eclipse.*;
+
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import lombok.core.DiagnosticsReceiver;
import lombok.core.PostCompiler;
+import lombok.core.AST.Kind;
import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
+import org.eclipse.jdt.internal.compiler.ast.Clinit;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
@@ -319,4 +346,200 @@ public class PatchFixes {
if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType;
return expr.resolveType(scope);
}
+
+ public static boolean handleDelegateForType(TypeDeclaration decl) {
+ if (decl.scope == null) return false;
+ if (decl.fields == null) return false;
+
+ for (FieldDeclaration field : decl.fields) {
+ if (field.annotations == null) continue;
+ for (Annotation ann : field.annotations) {
+ if (ann.type == null) continue;
+ TypeBinding tb = ann.type.resolveType(decl.initializerScope);
+ if (!charArrayEquals("lombok", tb.qualifiedPackageName())) continue;
+ if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) continue;
+
+ List<ClassLiteralAccess> rawTypes = new ArrayList<ClassLiteralAccess>();
+ for (MemberValuePair pair : ann.memberValuePairs()) {
+ if (pair.name == null || charArrayEquals("value", pair.name)) {
+ if (pair.value instanceof ArrayInitializer) {
+ for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
+ if (expr instanceof ClassLiteralAccess) rawTypes.add((ClassLiteralAccess) expr);
+ }
+ }
+ if (pair.value instanceof ClassLiteralAccess) {
+ rawTypes.add((ClassLiteralAccess) pair.value);
+ }
+ }
+ }
+
+ List<MethodBinding> methodsToDelegate = new ArrayList<MethodBinding>();
+
+ if (rawTypes.isEmpty()) {
+ addAllMethodBindings(methodsToDelegate, field.type.resolveType(decl.initializerScope));
+ } else {
+ for (ClassLiteralAccess cla : rawTypes) {
+ addAllMethodBindings(methodsToDelegate, cla.type.resolveType(decl.initializerScope));
+ }
+ }
+
+ System.out.println("About to generate the following methods, all delegating to: this." + new String(field.name));
+ for (MethodBinding mb : methodsToDelegate) {
+ System.out.println(mb);
+ }
+ System.out.println("-----------");
+
+ generateDelegateMethods(decl, methodsToDelegate, field.name, ann);
+ }
+ }
+
+ return false;
+ }
+
+ private static final Method methodScopeCreateMethodMethod;
+ private static final Field sourceTypeBindingMethodsField;
+
+ static {
+ Method m = null;
+ Field f = null;
+ Exception ex = null;
+
+ try {
+ m = MethodScope.class.getDeclaredMethod("createMethod", AbstractMethodDeclaration.class);
+ m.setAccessible(true);
+ f = SourceTypeBinding.class.getDeclaredField("methods");
+ f.setAccessible(true);
+ } catch (Exception e) {
+ ex = e;
+ }
+
+ methodScopeCreateMethodMethod = m;
+ sourceTypeBindingMethodsField = f;
+ if (ex != null) throw new RuntimeException(ex);
+ }
+
+ private static void generateDelegateMethods(TypeDeclaration type, List<MethodBinding> methods, char[] delegate, ASTNode source) {
+ for (MethodBinding binding : methods) {
+ MethodDeclaration method = generateDelegateMethod(delegate, binding, type.compilationResult, source);
+ if (type.methods == null) {
+ type.methods = new AbstractMethodDeclaration[1];
+ type.methods[0] = method;
+ } else {
+ int insertionPoint;
+ for (insertionPoint = 0; insertionPoint < type.methods.length; insertionPoint++) {
+ AbstractMethodDeclaration current = type.methods[insertionPoint];
+ if (current instanceof Clinit) continue;
+ if (Eclipse.isGenerated(current)) continue;
+ break;
+ }
+ AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[type.methods.length + 1];
+ System.arraycopy(type.methods, 0, newArray, 0, insertionPoint);
+ if (insertionPoint <= type.methods.length) {
+ System.arraycopy(type.methods, insertionPoint, newArray, insertionPoint + 1, type.methods.length - insertionPoint);
+ }
+
+ newArray[insertionPoint] = method;
+ type.methods = newArray;
+ MethodScope methodScope = new MethodScope(type.scope, method, false);
+
+ try {
+ MethodBinding methodBinding = (MethodBinding) methodScopeCreateMethodMethod.invoke(methodScope, method);
+ System.out.println("SCOPE NOW: " + method.scope);
+
+ method.resolve(type.scope);
+ System.out.println("Bind now: " + methodBinding.returnType);
+
+ MethodBinding[] existing = (MethodBinding[]) sourceTypeBindingMethodsField.get(type.binding);
+ if (existing == null) existing = new MethodBinding[] {methodBinding};
+ else {
+ MethodBinding[] copy = new MethodBinding[existing.length + 1];
+ System.arraycopy(existing, 0, copy, 0, existing.length);
+ copy[existing.length] = methodBinding;
+ }
+ sourceTypeBindingMethodsField.set(type.binding, existing);
+ System.out.println("Added method binding: " + methodBinding);
+ System.out.println(method);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private static MethodDeclaration generateDelegateMethod(char[] name, MethodBinding binding, CompilationResult compilationResult, ASTNode source) {
+ MethodDeclaration method = new MethodDeclaration(compilationResult);
+ Eclipse.setGeneratedBy(method, source);
+ method.modifiers = ClassFileConstants.AccPublic;
+ method.returnType = Eclipse.makeType(binding.returnType, source, false);
+ method.annotations = EclipseHandlerUtil.createSuppressWarningsAll(source, null);
+ if (binding.parameters != null && binding.parameters.length > 0) {
+ method.arguments = new Argument[binding.parameters.length];
+ for (int i = 0; i < method.arguments.length; i++) {
+ String argName = "$p" + i;
+ method.arguments[i] = new Argument(
+ argName.toCharArray(), pos(source),
+ Eclipse.makeType(binding.parameters[i], source, false),
+ ClassFileConstants.AccFinal);
+ }
+ }
+ method.selector = binding.selector;
+ if (binding.thrownExceptions != null && binding.thrownExceptions.length > 0) {
+ method.thrownExceptions = new TypeReference[binding.thrownExceptions.length];
+ for (int i = 0; i < method.thrownExceptions.length; i++) {
+ method.thrownExceptions[i] = Eclipse.makeType(binding.thrownExceptions[i], source, false);
+ }
+ }
+
+ method.typeParameters = null; // TODO think about this
+ method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ FieldReference fieldRef = new FieldReference(name, pos(source));
+ fieldRef.receiver = new ThisReference(source.sourceStart, source.sourceEnd);
+ MessageSend call = new MessageSend();
+ call.receiver = fieldRef;
+ call.selector = binding.selector;
+ if (method.arguments != null) {
+ call.arguments = new Expression[method.arguments.length];
+ for (int i = 0; i < method.arguments.length; i++) {
+ call.arguments[i] = new SingleNameReference(("$p" + i).toCharArray(), pos(source));
+ }
+ }
+
+ Statement body;
+ if (method.returnType instanceof SingleTypeReference && ((SingleTypeReference)method.returnType).token == TypeConstants.VOID) {
+ body = call;
+ } else {
+ body = new ReturnStatement(call, source.sourceStart, source.sourceEnd);
+ }
+
+ method.statements = new Statement[] {body};
+ // TODO add Eclipse.setGeneratedBy everywhere.
+ method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
+ method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
+ return method;
+ }
+
+ private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding) {
+ if (binding instanceof ReferenceBinding) {
+ for (MethodBinding mb : ((ReferenceBinding)binding).availableMethods()) {
+ if (mb.isStatic()) continue;
+ if (mb.isBridge()) continue;
+ if (mb.isConstructor()) continue;
+ if (mb.isDefaultAbstract()) continue;
+ if (!mb.isPublic()) continue;
+ if (mb.isSynthetic()) continue;
+ list.add(mb);
+ }
+ }
+ }
+
+ private static boolean charArrayEquals(String s, char[] c) {
+ if (s == null) return c == null;
+ if (c == null) return false;
+
+ if (s.length() != c.length) return false;
+ for (int i = 0; i < s.length(); i++) if (s.charAt(i) != c[i]) return false;
+ return true;
+
+
+ }
}