aboutsummaryrefslogtreecommitdiff
path: root/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/eclipseAgent/lombok/eclipse/agent/PatchVal.java')
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java328
1 files changed, 19 insertions, 309 deletions
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;