mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-15 02:17:12 +00:00
Mostly fruitless work on making delegate work in eclipse.
This commit is contained in:
1 parent
eae3e45ae7
commit
6c6f4c4642
5 files changed
+237
-3
No files matched your search
@@ -22,5 +22,5 @@
|
||||
package lombok;
|
||||
|
||||
public @interface Delegate {
|
||||
Class<?> value() default java.lang.Object.class;
|
||||
Class<?>[] value() default {};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user