aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java115
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java15
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java3
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java328
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java264
5 files changed, 395 insertions, 330 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index ea53835a..f28ba5a1 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2009-2010 Reinier Zwitserloot and Roel Spilker.
+ * Copyright © 2009-2011 Reinier Zwitserloot and Roel Spilker.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -43,9 +43,31 @@ import lombok.patcher.scripts.ScriptBuilder;
* transformed.
*/
public class EclipsePatcher extends Agent {
+ // At some point I'd like the agent to be capable of auto-detecting if its on eclipse or on ecj. This class is a sure sign we're not in ecj but in eclipse. -ReinierZ
+ @SuppressWarnings("unused")
+ private static final String ECLIPSE_SIGNATURE_CLASS = "org/eclipse/core/runtime/adaptor/EclipseStarter";
+
@Override
public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception {
- registerPatchScripts(instrumentation, injected, injected);
+ String[] args = agentArgs == null ? new String[0] : agentArgs.split(":");
+ boolean forceEcj = false;
+ boolean forceEclipse = false;
+ for (String arg : args) {
+ if (arg.trim().equalsIgnoreCase("ECJ")) forceEcj = true;
+ if (arg.trim().equalsIgnoreCase("ECLIPSE")) forceEclipse = true;
+ }
+ if (forceEcj && forceEclipse) {
+ forceEcj = false;
+ forceEclipse = false;
+ }
+
+ boolean ecj;
+
+ if (forceEcj) ecj = true;
+ else if (forceEclipse) ecj = false;
+ else ecj = injected;
+
+ registerPatchScripts(instrumentation, injected, ecj);
}
private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly) {
@@ -274,8 +296,93 @@ public class EclipsePatcher extends Agent {
}
private static void patchEcjTransformers(ScriptManager sm, boolean ecj) {
- PatchDelegate.addPatches(sm, ecj);
- PatchVal.addPatches(sm, ecj);
+ addPatchesForDelegate(sm, ecj);
+ addPatchesForVal(sm);
+ if (!ecj) addPatchesForValEclipse(sm);
+ }
+
+ private static void addPatchesForDelegate(ScriptManager sm, boolean ecj) {
+ final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope";
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
+ .request(StackRequest.THIS)
+ .decisionMethod(new Hook(PatchDelegate.class.getName(), "handleDelegateForType", "boolean", CLASSSCOPE_SIG))
+ .build());
+ }
+
+ private static void addPatchesForValEclipse(ScriptManager sm) {
+ final String LOCALDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration";
+ final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser";
+ final String VARIABLEDECLARATIONSTATEMENT_SIG = "org.eclipse.jdt.core.dom.VariableDeclarationStatement";
+ final String ASTCONVERTER_SIG = "org.eclipse.jdt.core.dom.ASTConverter";
+
+ sm.addScript(ScriptBuilder.addField()
+ .fieldName("$initCopy")
+ .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
+ .setPublic()
+ .setTransient()
+ .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
+ .build());
+
+ sm.addScript(ScriptBuilder.addField()
+ .fieldName("$iterableCopy")
+ .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
+ .setPublic()
+ .setTransient()
+ .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapReturnValue()
+ .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
+ .request(StackRequest.THIS)
+ .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfLocalDeclaration", "void", PARSER_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapReturnValue()
+ .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
+ .request(StackRequest.THIS)
+ .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfForEachIterable", "void", PARSER_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapReturnValue()
+ .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
+ .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "addFinalAndValAnnotationToVariableDeclarationStatement",
+ "void", "java.lang.Object", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
+ .transplant().request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
+ }
+
+ private static void addPatchesForVal(ScriptManager sm) {
+ final String LOCALDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration";
+ final String FOREACHSTATEMENT_SIG = "org.eclipse.jdt.internal.compiler.ast.ForeachStatement";
+ final String EXPRESSION_SIG = "org.eclipse.jdt.internal.compiler.ast.Expression";
+ final String BLOCKSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
+ final String TYPEBINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .request(StackRequest.THIS, StackRequest.PARAM1)
+ .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.replaceMethodCall()
+ .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
+ .requestExtra(StackRequest.THIS)
+ .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.replaceMethodCall()
+ .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
+ .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .request(StackRequest.THIS, StackRequest.PARAM1)
+ .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
+ .build());
}
private static void patchFixSourceTypeConverter(ScriptManager sm) {
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
index ea54be48..5680e8e1 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
@@ -37,11 +37,6 @@ import lombok.eclipse.EclipseAST;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.TransformEclipseAST;
import lombok.eclipse.handlers.EclipseHandlerUtil;
-import lombok.patcher.Hook;
-import lombok.patcher.MethodTarget;
-import lombok.patcher.ScriptManager;
-import lombok.patcher.StackRequest;
-import lombok.patcher.scripts.ScriptBuilder;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -84,16 +79,6 @@ import org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
public class PatchDelegate {
- static void addPatches(ScriptManager sm, boolean ecj) {
- final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope";
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
- .request(StackRequest.THIS)
- .decisionMethod(new Hook(PatchDelegate.class.getName(), "handleDelegateForType", "boolean", CLASSSCOPE_SIG))
- .build());
- }
-
private static class ClassScopeEntry {
ClassScopeEntry(ClassScope scope) {
this.scope = scope;
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
index b624caff..4c2b088d 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
@@ -35,7 +35,6 @@ import lombok.core.PostCompiler;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
@@ -144,7 +143,7 @@ public class PatchFixes {
try {
in = annotatable.getAnnotations();
- } catch (JavaModelException e) {
+ } catch (Exception e) {
return out;
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index 68b90286..b19e0742 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright © 2010 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans.
+ * Copyright © 2010-2011 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -18,34 +18,13 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
- *
- * Thanks to Stephen Haberman for a patch to solve some NPEs in Eclipse.
*/
package lombok.eclipse.agent;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.List;
-import lombok.Lombok;
import lombok.eclipse.Eclipse;
-import lombok.patcher.Hook;
-import lombok.patcher.MethodTarget;
-import lombok.patcher.ScriptManager;
-import lombok.patcher.StackRequest;
-import lombok.patcher.scripts.ScriptBuilder;
-import org.eclipse.jdt.core.dom.AST;
-import org.eclipse.jdt.core.dom.IExtendedModifier;
-import org.eclipse.jdt.core.dom.MarkerAnnotation;
-import org.eclipse.jdt.core.dom.Modifier;
-import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
-import org.eclipse.jdt.core.dom.Name;
-import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
-import org.eclipse.jdt.internal.compiler.ast.ASTNode;
-import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
@@ -62,275 +41,15 @@ import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
-import org.eclipse.jdt.internal.compiler.parser.Parser;
public class PatchVal {
+ // This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj.
// 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),
// and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one.
- static void addPatches(ScriptManager sm, boolean ecj) {
- final String LOCALDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration";
- final String FOREACHSTATEMENT_SIG = "org.eclipse.jdt.internal.compiler.ast.ForeachStatement";
- final String EXPRESSION_SIG = "org.eclipse.jdt.internal.compiler.ast.Expression";
- final String BLOCKSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
- final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser";
- final String TYPEBINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
- final String VARIABLEDECLARATIONSTATEMENT_SIG = "org.eclipse.jdt.core.dom.VariableDeclarationStatement";
- final String ASTCONVERTER_SIG = "org.eclipse.jdt.core.dom.ASTConverter";
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.replaceMethodCall()
- .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .requestExtra(StackRequest.THIS)
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.replaceMethodCall()
- .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
- .build());
-
- if (!ecj) {
- sm.addScript(ScriptBuilder.addField()
- .fieldName("$initCopy")
- .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
- .setPublic()
- .setTransient()
- .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
- .build());
-
- sm.addScript(ScriptBuilder.addField()
- .fieldName("$iterableCopy")
- .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
- .setPublic()
- .setTransient()
- .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
- .build());
-
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
- .request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfLocalDeclaration", "void", PARSER_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
- .request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfForEachIterable", "void", PARSER_SIG))
- .build());
-
-
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "addFinalAndValAnnotationToVariableDeclarationStatement",
- "void", "java.lang.Object", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
- .transplant().request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
- }
- }
-
- public static final class Reflection {
- public static final Field initCopyField, iterableCopyField;
- public static final Field astStackField, astPtrField;
- public static final Constructor<Modifier> modifierConstructor;
- public static final Constructor<MarkerAnnotation> markerAnnotationConstructor;
- public static final Method astConverterRecordNodes;
-
- static {
- Field a = null, b = null, c = null, d = null;
- Constructor<Modifier> f = null;
- Constructor<MarkerAnnotation> g = null;
- Method h = null;
-
- try {
- a = LocalDeclaration.class.getDeclaredField("$initCopy");
- b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
- } catch (Throwable t) {
- //ignore - no $initCopy exists when running in ecj.
- }
-
- try {
- c = Parser.class.getDeclaredField("astStack");
- c.setAccessible(true);
- d = Parser.class.getDeclaredField("astPtr");
- d.setAccessible(true);
- f = Modifier.class.getDeclaredConstructor(AST.class);
- f.setAccessible(true);
- g = MarkerAnnotation.class.getDeclaredConstructor(AST.class);
- g.setAccessible(true);
- Class<?> z = Class.forName("org.eclipse.jdt.core.dom.ASTConverter");
- h = z.getDeclaredMethod("recordNodes", org.eclipse.jdt.core.dom.ASTNode.class, org.eclipse.jdt.internal.compiler.ast.ASTNode.class);
- h.setAccessible(true);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- }
-
- initCopyField = a;
- iterableCopyField = b;
- astStackField = c;
- astPtrField = d;
- modifierConstructor = f;
- markerAnnotationConstructor = g;
- astConverterRecordNodes = h;
- }
- }
-
- public static void copyInitializationOfLocalDeclaration(Parser parser) {
- ASTNode[] astStack;
- int astPtr;
- try {
- astStack = (ASTNode[]) Reflection.astStackField.get(parser);
- astPtr = (Integer)Reflection.astPtrField.get(parser);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- return;
- }
- AbstractVariableDeclaration variableDecl = (AbstractVariableDeclaration) astStack[astPtr];
- if (!(variableDecl instanceof LocalDeclaration)) return;
- ASTNode init = variableDecl.initialization;
- if (init == null) return;
- if (variableDecl.type instanceof SingleTypeReference) {
- SingleTypeReference ref = (SingleTypeReference) variableDecl.type;
- if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
- } else return;
-
- try {
- if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init);
- } catch (Exception e) {
- // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
- }
- }
-
- public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, VariableDeclarationStatement out, LocalDeclaration in) {
- // First check that 'in' has the final flag on, and a @val / @lombok.val annotation.
- if ((in.modifiers & ClassFileConstants.AccFinal) == 0) return;
- if (in.annotations == null) return;
- boolean found = false;
- Annotation valAnnotation = null;
-
- for (Annotation ann : in.annotations) {
- if (ann.type instanceof SingleTypeReference) {
- if (matches("val", ((SingleTypeReference)ann.type).token)) {
- found = true;
- valAnnotation = ann;
- break;
- }
- }
- if (ann.type instanceof QualifiedTypeReference) {
- char[][] tokens = ((QualifiedTypeReference)ann.type).tokens;
- if (tokens != null && tokens.length == 2 && matches("lombok", tokens[0]) && matches("val", tokens[1])) {
- found = true;
- valAnnotation = ann;
- break;
- }
- }
- }
-
- if (!found) return;
-
- // Now check that 'out' is missing either of these.
-
- @SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers();
-
- if (modifiers == null) return; // This is null only if the project is 1.4 or less. Lombok doesn't work in that.
- boolean finalIsPresent = false;
- boolean valIsPresent = false;
-
- for (Object present : modifiers) {
- if (present instanceof Modifier) {
- ModifierKeyword keyword = ((Modifier)present).getKeyword();
- if (keyword == null) continue;
- if (keyword.toFlagValue() == Modifier.FINAL) finalIsPresent = true;
- }
-
- if (present instanceof org.eclipse.jdt.core.dom.Annotation) {
- Name typeName = ((org.eclipse.jdt.core.dom.Annotation) present).getTypeName();
- if (typeName != null) {
- String fullyQualifiedName = typeName.getFullyQualifiedName();
- if ("val".equals(fullyQualifiedName) || "lombok.val".equals(fullyQualifiedName)) {
- valIsPresent = true;
- }
- }
- }
- }
-
- if (!finalIsPresent) {
- modifiers.add(
- createModifier(out.getAST(), ModifierKeyword.FINAL_KEYWORD, valAnnotation.sourceStart, valAnnotation.sourceEnd));
- }
-
- if (!valIsPresent) {
- MarkerAnnotation newAnnotation = createValAnnotation(out.getAST(), valAnnotation, valAnnotation.sourceStart, valAnnotation.sourceEnd);
- try {
- Reflection.astConverterRecordNodes.invoke(converter, newAnnotation, valAnnotation);
- Reflection.astConverterRecordNodes.invoke(converter, newAnnotation.getTypeName(), valAnnotation.type);
- } catch (IllegalAccessException e) {
- Lombok.sneakyThrow(e);
- } catch (InvocationTargetException e) {
- Lombok.sneakyThrow(e.getCause());
- }
- modifiers.add(newAnnotation);
- }
- }
-
- public static MarkerAnnotation createValAnnotation(AST ast, Annotation original, int start, int end) {
- MarkerAnnotation out = null;
- try {
- out = Reflection.markerAnnotationConstructor.newInstance(ast);
- } catch (InstantiationException e) {
- Lombok.sneakyThrow(e);
- } catch (IllegalAccessException e) {
- Lombok.sneakyThrow(e);
- } catch (InvocationTargetException e) {
- Lombok.sneakyThrow(e);
- }
-
- if (out != null) {
- if (original.type instanceof SingleTypeReference) {
- out.setTypeName(ast.newSimpleName("val"));
- } else {
- out.setTypeName(ast.newQualifiedName(ast.newSimpleName("lombok"), ast.newSimpleName("val")));
- }
- out.setSourceRange(start, end - start + 1);
- }
-
- return out;
- }
-
- public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) {
- Modifier modifier = null;
- try {
- modifier = Reflection.modifierConstructor.newInstance(ast);
- } catch (InstantiationException e) {
- Lombok.sneakyThrow(e);
- } catch (IllegalAccessException e) {
- Lombok.sneakyThrow(e);
- } catch (InvocationTargetException e) {
- Lombok.sneakyThrow(e);
- }
-
- if (modifier != null) {
- modifier.setKeyword(keyword);
- modifier.setSourceRange(start, end - start + 1);
- }
- return modifier;
- }
-
public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
if (expr.resolvedType != null) return expr.resolvedType;
return expr.resolveType(scope);
@@ -377,6 +96,23 @@ public class PatchVal {
return matches("lombok", pkg) && matches("val", nm);
}
+ public static final class Reflection {
+ private static final Field initCopyField, iterableCopyField;
+
+ static {
+ Field a = null, b = null;
+
+ try {
+ a = LocalDeclaration.class.getDeclaredField("$initCopy");
+ b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ } catch (Throwable t) {
+ //ignore - no $initCopy exists when running in ecj.
+ }
+
+ initCopyField = a;
+ iterableCopyField = b;
+ }
+ }
public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false;
boolean decomponent = false;
@@ -417,32 +153,6 @@ public class PatchVal {
return false;
}
- public static void copyInitializationOfForEachIterable(Parser parser) {
- ASTNode[] astStack;
- int astPtr;
- try {
- astStack = (ASTNode[]) Reflection.astStackField.get(parser);
- astPtr = (Integer) Reflection.astPtrField.get(parser);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- return;
- }
-
- ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
- ASTNode init = foreachDecl.collection;
- if (init == null) return;
- if (foreachDecl.elementVariable != null && foreachDecl.elementVariable.type instanceof SingleTypeReference) {
- SingleTypeReference ref = (SingleTypeReference) foreachDecl.elementVariable.type;
- if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
- } else return;
-
- try {
- if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init);
- } catch (Exception e) {
- // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
- }
- }
-
public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
if (forEach.elementVariable == null) return false;
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
new file mode 100644
index 00000000..3fb407b9
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2010-2011 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Thanks to Stephen Haberman for a patch to solve some NPEs in Eclipse.
+ */
+package lombok.eclipse.agent;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import lombok.Lombok;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+
+public class PatchValEclipse {
+ public static void copyInitializationOfForEachIterable(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer) Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+
+ ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
+ ASTNode init = foreachDecl.collection;
+ if (init == null) return;
+ if (foreachDecl.elementVariable != null && foreachDecl.elementVariable.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) foreachDecl.elementVariable.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer)Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+ AbstractVariableDeclaration variableDecl = (AbstractVariableDeclaration) astStack[astPtr];
+ if (!(variableDecl instanceof LocalDeclaration)) return;
+ ASTNode init = variableDecl.initialization;
+ if (init == null) return;
+ if (variableDecl.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) variableDecl.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, VariableDeclarationStatement out, LocalDeclaration in) {
+ // First check that 'in' has the final flag on, and a @val / @lombok.val annotation.
+ if ((in.modifiers & ClassFileConstants.AccFinal) == 0) return;
+ if (in.annotations == null) return;
+ boolean found = false;
+ Annotation valAnnotation = null;
+
+ for (Annotation ann : in.annotations) {
+ if (ann.type instanceof SingleTypeReference) {
+ if (PatchVal.matches("val", ((SingleTypeReference)ann.type).token)) {
+ found = true;
+ valAnnotation = ann;
+ break;
+ }
+ }
+ if (ann.type instanceof QualifiedTypeReference) {
+ char[][] tokens = ((QualifiedTypeReference)ann.type).tokens;
+ if (tokens != null && tokens.length == 2 && PatchVal.matches("lombok", tokens[0]) && PatchVal.matches("val", tokens[1])) {
+ found = true;
+ valAnnotation = ann;
+ break;
+ }
+ }
+ }
+
+ if (!found) return;
+
+ // Now check that 'out' is missing either of these.
+
+ @SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers();
+
+ if (modifiers == null) return; // This is null only if the project is 1.4 or less. Lombok doesn't work in that.
+ boolean finalIsPresent = false;
+ boolean valIsPresent = false;
+
+ for (Object present : modifiers) {
+ if (present instanceof Modifier) {
+ ModifierKeyword keyword = ((Modifier)present).getKeyword();
+ if (keyword == null) continue;
+ if (keyword.toFlagValue() == Modifier.FINAL) finalIsPresent = true;
+ }
+
+ if (present instanceof org.eclipse.jdt.core.dom.Annotation) {
+ Name typeName = ((org.eclipse.jdt.core.dom.Annotation) present).getTypeName();
+ if (typeName != null) {
+ String fullyQualifiedName = typeName.getFullyQualifiedName();
+ if ("val".equals(fullyQualifiedName) || "lombok.val".equals(fullyQualifiedName)) {
+ valIsPresent = true;
+ }
+ }
+ }
+ }
+
+ if (!finalIsPresent) {
+ modifiers.add(
+ createModifier(out.getAST(), ModifierKeyword.FINAL_KEYWORD, valAnnotation.sourceStart, valAnnotation.sourceEnd));
+ }
+
+ if (!valIsPresent) {
+ MarkerAnnotation newAnnotation = createValAnnotation(out.getAST(), valAnnotation, valAnnotation.sourceStart, valAnnotation.sourceEnd);
+ try {
+ Reflection.astConverterRecordNodes.invoke(converter, newAnnotation, valAnnotation);
+ Reflection.astConverterRecordNodes.invoke(converter, newAnnotation.getTypeName(), valAnnotation.type);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e.getCause());
+ }
+ modifiers.add(newAnnotation);
+ }
+ }
+
+ public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) {
+ Modifier modifier = null;
+ try {
+ modifier = Reflection.modifierConstructor.newInstance(ast);
+ } catch (InstantiationException e) {
+ Lombok.sneakyThrow(e);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e);
+ }
+
+ if (modifier != null) {
+ modifier.setKeyword(keyword);
+ modifier.setSourceRange(start, end - start + 1);
+ }
+ return modifier;
+ }
+
+ public static MarkerAnnotation createValAnnotation(AST ast, Annotation original, int start, int end) {
+ MarkerAnnotation out = null;
+ try {
+ out = Reflection.markerAnnotationConstructor.newInstance(ast);
+ } catch (InstantiationException e) {
+ Lombok.sneakyThrow(e);
+ } catch (IllegalAccessException e) {
+ Lombok.sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ Lombok.sneakyThrow(e);
+ }
+
+ if (out != null) {
+ if (original.type instanceof SingleTypeReference) {
+ out.setTypeName(ast.newSimpleName("val"));
+ } else {
+ out.setTypeName(ast.newQualifiedName(ast.newSimpleName("lombok"), ast.newSimpleName("val")));
+ }
+ out.setSourceRange(start, end - start + 1);
+ }
+
+ return out;
+ }
+
+ public static final class Reflection {
+ private static final Field initCopyField, iterableCopyField;
+ private static final Field astStackField, astPtrField;
+ private static final Constructor<Modifier> modifierConstructor;
+ private static final Constructor<MarkerAnnotation> markerAnnotationConstructor;
+ private static final Method astConverterRecordNodes;
+
+ static {
+ Field a = null, b = null, c = null, d = null;
+ Constructor<Modifier> f = null;
+ Constructor<MarkerAnnotation> g = null;
+ Method h = null;
+
+ try {
+ a = LocalDeclaration.class.getDeclaredField("$initCopy");
+ b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ } catch (Throwable t) {
+ //ignore - no $initCopy exists when running in ecj.
+ }
+
+ try {
+ c = Parser.class.getDeclaredField("astStack");
+ c.setAccessible(true);
+ d = Parser.class.getDeclaredField("astPtr");
+ d.setAccessible(true);
+ f = Modifier.class.getDeclaredConstructor(AST.class);
+ f.setAccessible(true);
+ g = MarkerAnnotation.class.getDeclaredConstructor(AST.class);
+ g.setAccessible(true);
+ Class<?> z = Class.forName("org.eclipse.jdt.core.dom.ASTConverter");
+ h = z.getDeclaredMethod("recordNodes", org.eclipse.jdt.core.dom.ASTNode.class, org.eclipse.jdt.internal.compiler.ast.ASTNode.class);
+ h.setAccessible(true);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ }
+
+ initCopyField = a;
+ iterableCopyField = b;
+ astStackField = c;
+ astPtrField = d;
+ modifierConstructor = f;
+ markerAnnotationConstructor = g;
+ astConverterRecordNodes = h;
+ }
+ }
+}