mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-20 02:17:52 +00:00
Split up PatchVal into the ecj and eclipse bits; in ecj you'd just get NoSuchClassErrors.
This commit is contained in:
1 parent
8b2ee73cc4
commit
75e81604f3
5 files changed
+395
-330
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user