diff options
Diffstat (limited to 'src/eclipseAgent/lombok/eclipse')
10 files changed, 837 insertions, 354 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java index aa01c13d..25906c6b 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java @@ -31,6 +31,12 @@ import lombok.patcher.scripts.ScriptBuilder; public class EclipseLoaderPatcher { private static final String TRANSPLANTS_CLASS_NAME = "lombok.eclipse.agent.EclipseLoaderPatcherTransplants"; + static final String[] OSGI_TYPES = { + "org/eclipse/osgi/internal/baseadaptor/DefaultClassLoader", + "org/eclipse/osgi/framework/adapter/core/AbstractClassLoader", + "org/eclipse/osgi/internal/loader/ModuleClassLoader" + }; + public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingContext) { sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader", "loadClass", diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index c560f002..ce26c892 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2020 The Project Lombok Authors. + * Copyright (C) 2009-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,7 @@ package lombok.eclipse.agent; import static lombok.patcher.scripts.ScriptBuilder.*; +import static lombok.eclipse.agent.EclipseLoaderPatcher.OSGI_TYPES; import java.io.File; import java.lang.instrument.Instrumentation; @@ -49,34 +50,11 @@ import lombok.patcher.scripts.ScriptBuilder; * transformed. */ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { - // 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, Class<?> launchingContext) throws Exception { - 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, launchingContext); + registerPatchScripts(instrumentation, injected, launchingContext); } - private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) { + private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, Class<?> launchingContext) { ScriptManager sm = new ScriptManager(); sm.registerTransformer(instrumentation); sm.setFilter(new Filter() { @@ -97,33 +75,33 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } }); - if (!ecjOnly) { - EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext); - patchCatchReparse(sm); - patchIdentifierEndReparse(sm); - patchRetrieveEllipsisStartPosition(sm); - patchRetrieveRightBraceOrSemiColonPosition(sm); - patchRetrieveProperRightBracketPosition(sm); - patchSetGeneratedFlag(sm); - patchDomAstReparseIssues(sm); - patchHideGeneratedNodes(sm); - patchPostCompileHookEclipse(sm); - patchFixSourceTypeConverter(sm); - patchDisableLombokForCodeCleanup(sm); - patchListRewriteHandleGeneratedMethods(sm); - patchSyntaxAndOccurrencesHighlighting(sm); - patchSortMembersOperation(sm); - patchExtractInterface(sm); - patchAboutDialog(sm); - patchEclipseDebugPatches(sm); - } else { - patchPostCompileHookEcj(sm); - } + EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext); + patchCatchReparse(sm); + patchIdentifierEndReparse(sm); + patchRetrieveEllipsisStartPosition(sm); + patchRetrieveRightBraceOrSemiColonPosition(sm); + patchRetrieveProperRightBracketPosition(sm); + patchRetrieveStartBlockPosition(sm); + patchSetGeneratedFlag(sm); + patchDomAstReparseIssues(sm); + patchHideGeneratedNodes(sm); + patchPostCompileHookEclipse(sm); + patchFixSourceTypeConverter(sm); + patchDisableLombokForCodeCleanup(sm); + patchListRewriteHandleGeneratedMethods(sm); + patchSyntaxAndOccurrencesHighlighting(sm); + patchSortMembersOperation(sm); + patchExtractInterface(sm); + patchAboutDialog(sm); + patchEclipseDebugPatches(sm); + patchJavadoc(sm); + + patchPostCompileHookEcj(sm); patchAvoidReparsingGeneratedCode(sm); patchLombokizeAST(sm); - patchEcjTransformers(sm, ecjOnly); - patchExtensionMethod(sm, ecjOnly); + patchEcjTransformers(sm); + patchExtensionMethod(sm); patchRenameField(sm); patchNullCheck(sm); @@ -158,7 +136,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { private static void patchExtractInterface(ScriptManager sm) { /* Fix sourceEnding for generated nodes to avoid null pointer */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.SourceElementNotifier", "notifySourceElementRequestor", "void", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "org.eclipse.jdt.internal.compiler.ast.ImportReference")) .methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt", "get", "int", "java.lang.Object")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) @@ -166,7 +144,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Make sure the generated source element is found instead of the annotation */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodDeclaration", "void", "org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite", "org.eclipse.jdt.core.dom.rewrite.ASTRewrite", @@ -179,7 +157,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* get real generated node in stead of a random one generated by the annotation */ - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMemberDeclarations")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComments")) .methodToReplace(new Hook("org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil", "getMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit")) @@ -187,14 +165,14 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Do not add @Override's for generated methods */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ListRewrite", "insertFirst")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite")) .request(StackRequest.THIS) .transplant().build()); /* Do not add comments for generated methods */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComment")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) .request(StackRequest.PARAM2) @@ -207,7 +185,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { * This is doable without patching, but we intentionally patch it so that presence of the lombok info * in the about dialog can be used to ascertain that patching in general is doing something. */ - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.core.internal.runtime.Product", "getProperty", "java.lang.String", "java.lang.String")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String")) .request(StackRequest.RETURN_VALUE, StackRequest.PARAM1) @@ -218,7 +196,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { /* * Skip generated nodes for "visual effects" (syntax highlighting && highlight occurrences) */ - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addUsage")) .target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addWrite")) .target(new MethodTarget("org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingReconciler$PositionCollector", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) @@ -229,7 +207,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchDisableLombokForCodeCleanup(ScriptManager sm) { - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.DoStatement")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.EnhancedForStatement")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.ForStatement")) @@ -256,7 +234,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchListRewriteHandleGeneratedMethods(ScriptManager sm) { - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer$ListRewriter", "rewriteList")) .methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent", "getChildren", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]")) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent")) @@ -268,37 +246,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { * I would have liked to patch sortMembers, but kept getting a VerifyError: Illegal type in constant pool * So now I just patch all calling methods */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.CompilationUnit")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.CompilationUnit", "types", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnnotationTypeDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnnotationTypeDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnonymousClassDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnonymousClassDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.TypeDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "bodyDeclarations", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "enumConstants", "java.util.List")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List")) @@ -306,7 +284,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchDomAstReparseIssues(ScriptManager sm) { - sm.addScript(ScriptBuilder.replaceMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.replaceMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer", "visit")) .methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "getTokenEndOffset", "int", "int", "int")) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object")) @@ -317,7 +295,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchPostCompileHookEclipse(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents")) .target(new MethodTarget("org.eclipse.jdt.internal.core.builder.AbstractImageBuilder", "writeClassFileContents")) .methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.ClassFile", "getBytes", "byte[]")) @@ -327,13 +305,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchPostCompileHookEcj(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfNotWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl", "outputClassFiles")) .methodToWrap(new Hook("javax.tools.JavaFileObject", "openOutputStream", "java.io.OutputStream")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfNotWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.compiler.util.Util", "writeToDisk")) .methodToWrap(new Hook("java.io.BufferedOutputStream", "<init>", "void", "java.io.OutputStream", "int")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String")) @@ -342,7 +320,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchHideGeneratedNodes(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByNode")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByBinding")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]", @@ -354,37 +332,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchFormatters(ScriptManager sm) { - // before Eclipse Mars - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) - .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) - .symbol("lombok.disable") - .build()); + // before Eclipse Mars + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit")) + .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean")) + .symbol("lombok.disable") + .build()); // Eclipse Mars and beyond - sm.addScript(ScriptBuilder.setSymbolDuringMethodCall() - .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode")) - .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor")) - .symbol("lombok.disable") - .build()); + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode")) + .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor")) + .symbol("lombok.disable") + .build()); } private static void patchRefactorScripts(ScriptManager sm) { - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "replace")) .target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "remove")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "skipRewritingGeneratedNodes", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) .transplant().request(StackRequest.PARAM1).build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor", "addConstructorRenames")) .methodToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]", "org.eclipse.jdt.core.IMethod[]")) .transplant().build()); - sm.addScript(ScriptBuilder.exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.exitEarly() .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameAnalyzeUtil$ProblemNodeFinder$NameNodeVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName")) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode")) @@ -401,21 +379,28 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchIdentifierEndReparse(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveIdentifierEndPosition")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int", "int")) .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM1, StackRequest.PARAM2).build()); } private static void patchRetrieveEllipsisStartPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveEllipsisStartPosition")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int")) .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); } + private static void patchRetrieveStartBlockPosition(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveStartBlockPosition")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveStartBlockPosition", "int", "int", "int")) + .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build()); + } + private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition", "int", "int", "int")) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) @@ -447,28 +432,36 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } private static void patchRetrieveProperRightBracketPosition(ScriptManager sm) { - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "extractSubArrayType", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.ArrayType", "int", "int")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.ArrayType")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) + .requestExtra(StackRequest.PARAM1) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToArray", "org.eclipse.jdt.core.dom.ArrayType", "org.eclipse.jdt.core.dom.Type", "int", "int", "int", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveProperRightBracketPosition", "int", "int", "int")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveProperRightBracketPosition", "int", "int", "org.eclipse.jdt.core.dom.Type")) .requestExtra(StackRequest.PARAM1) .transplant() .build()); } private static void patchSetGeneratedFlag(ScriptManager sm) { - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .targetClass("org.eclipse.jdt.internal.compiler.ast.ASTNode") .fieldName("$generatedBy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic().setTransient().build()); - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .targetClass("org.eclipse.jdt.core.dom.ASTNode") .fieldName("$isGenerated").fieldType("Z") .setPublic().setTransient().build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new TargetMatcher() { @Override public boolean matches(String classSpec, String methodName, String descriptor) { if (!"convert".equals(methodName)) return false; @@ -480,6 +473,10 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { return true; } + @Override public String describe() { + return "ASTConverter:[all relevant]"; + } + @Override public Collection<String> getAffectedClasses() { return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter"); } @@ -488,14 +485,14 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) .request(StackRequest.PARAM2, StackRequest.RETURN_VALUE) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToFieldDeclaration", "org.eclipse.jdt.core.dom.FieldDeclaration", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) /* Targets beneath are only patched because the resulting dom nodes should be marked if generated. * However I couldn't find a usecase where these were actually used @@ -507,6 +504,8 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationFragment", "org.eclipse.jdt.core.dom.VariableDeclarationFragment", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationStatement", "org.eclipse.jdt.core.dom.VariableDeclarationStatement", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createBaseType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "int", "boolean")) + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "createQualifiedType", "org.eclipse.jdt.core.dom.QualifiedType", "org.eclipse.jdt.internal.compiler.ast.TypeReference", "long[]", "org.eclipse.jdt.internal.compiler.ast.Annotation[][]", "char[][]", "int", "org.eclipse.jdt.core.dom.Type")) /* Targets above are only patched because the resulting dom nodes should be marked if generated. */ .request(StackRequest.PARAM1, StackRequest.RETURN_VALUE) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", @@ -514,7 +513,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .transplant().build()); /* Set generated flag for SimpleNames */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new TargetMatcher() { @Override public boolean matches(String classSpec, String methodName, String descriptor) { if (!methodName.startsWith("convert")) return false; @@ -526,6 +525,10 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { return true; } + @Override public String describe() { + return "ASTConverter::(all relevant)"; + } + @Override public Collection<String> getAffectedClasses() { return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter"); } @@ -535,16 +538,53 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM2) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void", "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); + + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.PrimitiveType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ParameterizedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertType", "org.eclipse.jdt.core.dom.Type", "org.eclipse.jdt.internal.compiler.ast.TypeReference")) + .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedType", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) + .requestExtra(StackRequest.PARAM1) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void", + "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) + .transplant() + .build()); /* Set generated flag for QualifiedNames */ - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) @@ -552,7 +592,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM4) @@ -560,7 +600,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM3) @@ -568,7 +608,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM3) @@ -576,7 +616,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setTypeNameForAnnotation", "void", "org.eclipse.jdt.internal.compiler.ast.Annotation", "org.eclipse.jdt.core.dom.Annotation")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM1) @@ -584,7 +624,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { "org.eclipse.jdt.core.dom.Name", "java.lang.Object")) .transplant().build()); - sm.addScript(ScriptBuilder.wrapMethodCall() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setTypeNameForAnnotation", "void", "org.eclipse.jdt.internal.compiler.ast.Annotation", "org.eclipse.jdt.core.dom.Annotation")) .methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST")) .requestExtra(StackRequest.PARAM1) @@ -629,25 +669,26 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser"; final String CUD_SIG = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "getMethodBodies", "void", CUD_SIG)) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", PARSER_SIG, CUD_SIG)) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", OBJECT_SIG, OBJECT_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1).build()); sm.addScript(ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "endParse", CUD_SIG, "int")) - .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", CUD_SIG, PARSER_SIG)) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", OBJECT_SIG, OBJECT_SIG)) .request(StackRequest.THIS, StackRequest.RETURN_VALUE).build()); } - private static void patchEcjTransformers(ScriptManager sm, boolean ecj) { - addPatchesForDelegate(sm, ecj); + private static void patchEcjTransformers(ScriptManager sm) { + addPatchesForDelegate(sm); addPatchesForVal(sm); - if (!ecj) addPatchesForValEclipse(sm); + addPatchesForValEclipse(sm); } - private static void addPatchesForDelegate(ScriptManager sm, boolean ecj) { + private static void addPatchesForDelegate(ScriptManager sm) { final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope"; sm.addScript(ScriptBuilder.exitEarly() @@ -655,6 +696,26 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.THIS) .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object")) .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.setSymbolDuringMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.core.SelectionRequestor", "acceptSourceMethod")) + .callToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]")) + .symbol("lombok.skipdelegates") + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() + .fieldName("$delegateMethods") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() + .target(new MethodTarget("org.eclipse.jdt.internal.core.SourceTypeElementInfo", "getChildren", "org.eclipse.jdt.core.IJavaElement[]")) + .request(StackRequest.RETURN_VALUE, StackRequest.THIS) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "addGeneratedDelegateMethods", "java.lang.Object[]", "java.lang.Object", "java.lang.Object")) + .build()); } private static void addPatchesForValEclipse(ScriptManager sm) { @@ -664,7 +725,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String SINGLEVARIABLEDECLARATION_SIG = "org.eclipse.jdt.core.dom.SingleVariableDeclaration"; final String ASTCONVERTER_SIG = "org.eclipse.jdt.core.dom.ASTConverter"; - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .fieldName("$initCopy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic() @@ -672,7 +733,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") .build()); - sm.addScript(ScriptBuilder.addField() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() .fieldName("$iterableCopy") .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;") .setPublic() @@ -680,25 +741,25 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void")) .request(StackRequest.THIS) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object")) .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void")) .request(StackRequest.THIS) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object")) .build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToVariableDeclarationStatement", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object")) .request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build()); - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", SINGLEVARIABLEDECLARATION_SIG, LOCALDECLARATION_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToSingleVariableDeclaration", "void", "java.lang.Object", "java.lang.Object", "java.lang.Object")) @@ -711,11 +772,12 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { 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"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1) - .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG)) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", OBJECT_SIG, OBJECT_SIG)) .build()); sm.addScript(ScriptBuilder.replaceMethodCall() @@ -723,18 +785,20 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG)) .requestExtra(StackRequest.THIS) .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG)) + .transplant() .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.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG)) + .transplant() .build()); sm.addScript(ScriptBuilder.exitEarly() .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG)) .request(StackRequest.THIS, StackRequest.PARAM1) - .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG)) + .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", OBJECT_SIG, OBJECT_SIG)) .build()); } @@ -743,7 +807,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String I_ANNOTATABLE_SIG = "org.eclipse.jdt.core.IAnnotatable"; final String ANNOTATION_SIG = "org.eclipse.jdt.internal.compiler.ast.Annotation"; - sm.addScript(ScriptBuilder.wrapReturnValue() + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapReturnValue() .target(new MethodTarget(SOURCE_TYPE_CONVERTER_SIG, "convertAnnotations", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG)) .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG)) .request(StackRequest.PARAM1, StackRequest.RETURN_VALUE).build()); @@ -753,7 +817,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String ASTNODE_SIG = "org.eclipse.jdt.core.dom.ASTNode"; final String PATCH_DEBUG = "lombok.eclipse.agent.PatchDiagnostics"; - sm.addScript(exitEarly() + sm.addScriptIfWitness(OSGI_TYPES, exitEarly() .target(new MethodTarget(ASTNODE_SIG, "setSourceRange", "void", "int", "int")) .request(StackRequest.THIS) .request(StackRequest.PARAM1) @@ -762,7 +826,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .build()); } - private static void patchExtensionMethod(ScriptManager sm, boolean ecj) { + private static void patchExtensionMethod(ScriptManager sm) { final String PATCH_EXTENSIONMETHOD = "lombok.launch.PatchFixesHider$ExtensionMethod"; final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal"; final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; @@ -774,42 +838,50 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { final String METHOD_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; final String COMPLETION_PROPOSAL_COLLECTOR_SIG = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector"; final String I_JAVA_COMPLETION_PROPOSAL_SIG = "org.eclipse.jdt.ui.text.java.IJavaCompletionProposal[]"; + final String AST_NODE = "org.eclipse.jdt.internal.compiler.ast.ASTNode"; + final String OBJECT_SIG = "java.lang.Object"; sm.addScript(wrapReturnValue() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .request(StackRequest.RETURN_VALUE) .request(StackRequest.THIS) .request(StackRequest.PARAM1) - .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD, "resolveType", TYPE_BINDING_SIG, TYPE_BINDING_SIG, MESSAGE_SEND_SIG, BLOCK_SCOPE_SIG)) + .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD, "resolveType", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) + .cast() .build()); sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "errorNoMethodFor", "void", MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "errorNoMethodFor", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, TYPE_BINDING_SIG, TYPE_BINDINGS_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "errorNoMethodFor", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); // Since eclipse mars; they added a param. sm.addScript(replaceMethodCall() .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) - .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) .build()); - if (!ecj) { - sm.addScript(wrapReturnValue() - .target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG)) - .request(StackRequest.RETURN_VALUE) - .request(StackRequest.THIS) - .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG, "java.lang.Object[]", "java.lang.Object")) - .build()); - } + sm.addScript(replaceMethodCall() + .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG)) + .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "nonStaticAccessToStaticMethod", "void", AST_NODE, METHOD_BINDING_SIG)) + .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "nonStaticAccessToStaticMethod", "void", OBJECT_SIG, OBJECT_SIG, OBJECT_SIG, OBJECT_SIG)) + .requestExtra(StackRequest.THIS) + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, wrapReturnValue() + .target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG)) + .request(StackRequest.RETURN_VALUE) + .request(StackRequest.THIS) + .wrapMethod(new Hook(PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG, "java.lang.Object[]", "java.lang.Object")) + .build()); } private static void patchNullCheck(ScriptManager sm) { @@ -830,5 +902,37 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { .request(StackRequest.PARAM1) .transplant().build()); } + + private static void patchJavadoc(ScriptManager sm) { + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IJavaElement", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + /* This is an older version that uses IMember instead of IJavaElement */ + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.wrapMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContent", "java.lang.String", "org.eclipse.jdt.core.IMember", "boolean")) + .methodToWrap(new Hook("org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2", "getHTMLContentFromSource", "java.lang.String", "org.eclipse.jdt.core.IMember")) + .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "getHTMLContentFromSource", "java.lang.String", "java.lang.String", "org.eclipse.jdt.core.IJavaElement")) + .requestExtra(StackRequest.PARAM1) + .build()); + + sm.addScript(ScriptBuilder.replaceMethodCall() + .target(new MethodTarget("org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "printBody", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .methodToReplace(new Hook("org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "print", "java.lang.StringBuffer", "int", "java.lang.StringBuffer")) + .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Javadoc", "printMethod", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "int", "java.lang.StringBuffer", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration")) + .requestExtra(StackRequest.THIS) + .build()); + + sm.addScriptIfWitness(OSGI_TYPES, ScriptBuilder.addField() + .fieldName("$javadoc") + .fieldType("Ljava/util/Map;") + .setPublic() + .setTransient() + .targetClass("org.eclipse.jdt.internal.core.CompilationUnit") + .build()); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java index 46ce63f9..833a8226 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/ExtensionMethodCompletionProposal.java @@ -22,10 +22,15 @@ package lombok.eclipse.agent; import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccStatic; +import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.AccVarargs; import java.util.Arrays; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.CompletionProposal; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal; @@ -35,16 +40,29 @@ import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameRefere import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.core.NameLookup; public class ExtensionMethodCompletionProposal extends InternalCompletionProposal { + private char[] fullSignature; + private char[][] parameterNames; + public ExtensionMethodCompletionProposal(final int replacementOffset) { super(CompletionProposal.METHOD_REF, replacementOffset - 1); } public void setMethodBinding(final MethodBinding method, final ASTNode node) { + // Add proposal parameter names, sometimes its empty... + if (method.parameterNames != null && method.parameterNames.length > 0) { + setParameterNames(Arrays.copyOfRange(method.parameterNames, 1, method.parameterNames.length)); + } else { + // Copy signature for parameter name resolution, this is more reliable but slower + fullSignature = CompletionEngine.getSignature(method); + } + MethodBinding original = method.original(); TypeBinding[] parameters = Arrays.copyOf(method.parameters, method.parameters.length); method.parameters = Arrays.copyOfRange(method.parameters, 1, method.parameters.length); + TypeBinding[] originalParameters = null; if (original != method) { originalParameters = Arrays.copyOf(method.original().parameters, method.original().parameters.length); @@ -76,6 +94,10 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa setName(method.selector); setCompletion(completion); setFlags(method.modifiers & (~AccStatic)); + // Remove varargs flag if it is the only parameter + if (method.isVarargs() && length == 0) { + setFlags(getFlags() & (~AccVarargs)); + } int index = node.sourceEnd + 1; if (node instanceof CompletionOnQualifiedNameReference) { index -= ((CompletionOnQualifiedNameReference) node).completionIdentifier.length; @@ -96,4 +118,58 @@ public class ExtensionMethodCompletionProposal extends InternalCompletionProposa method.original().parameters = originalParameters; } } + + @Override + public char[][] findParameterNames(IProgressMonitor monitor) { + if (parameterNames != null) { + return parameterNames; + } + + NameLookup.Answer answer = this.nameLookup.findType( + new String(this.declarationTypeName), + new String(this.declarationPackageName), + false, + NameLookup.ACCEPT_CLASSES & NameLookup.ACCEPT_INTERFACES, + true/* consider secondary types */, + false/* do NOT wait for indexes */, + false/*don't check restrictions*/, + null); + + if (answer != null && answer.type != null) { + char[][] parameterTypes = Signature.getParameterTypes(fullSignature); + + String[] args = new String[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + args[i] = new String(parameterTypes[i]); + } + IMethod method = answer.type.getMethod(new String(this.getName()), args); + IMethod[] methods = answer.type.findMethods(method); + if (methods != null && methods.length > 0) { + method = methods[0]; + } + if (method != null) { + try { + String[] parameterNames = method.getParameterNames(); + char[][] parameterNamesAsChar = new char[parameterNames.length - 1][]; + for (int i = 0; i < parameterNamesAsChar.length; i++) { + parameterNamesAsChar[i] = parameterNames[i + 1].toCharArray(); + } + setParameterNames(parameterNamesAsChar); + } catch (JavaModelException e) { + // Nope + } + } + } + // Seems like we failed, fallback + if (parameterNames == null) { + parameterNames = super.findParameterNames(monitor); + } + return parameterNames; + } + + @Override + public void setParameterNames(char[][] parameterNames) { + this.parameterNames = parameterNames; + super.setParameterNames(parameterNames); + } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java index 1a287d93..d7b17598 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,8 +22,8 @@ package lombok.eclipse.agent; import static lombok.eclipse.Eclipse.*; +import static lombok.eclipse.EcjAugments.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import static lombok.eclipse.EclipseAugments.Annotation_applied; import java.lang.reflect.Method; import java.util.ArrayList; @@ -34,14 +34,16 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -import lombok.core.AST.Kind; -import lombok.eclipse.Eclipse; -import lombok.eclipse.EclipseAST; -import lombok.eclipse.EclipseNode; -import lombok.eclipse.TransformEclipseAST; -import lombok.eclipse.handlers.SetGeneratedByVisitor; - +import org.eclipse.jdt.core.ElementChangedEvent; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -81,8 +83,28 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.UnresolvedReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.DeltaProcessor; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaElementDelta; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.LocalVariable; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.core.SourceMethodInfo; +import org.eclipse.jdt.internal.core.SourceType; +import org.eclipse.jdt.internal.core.SourceTypeElementInfo; + +import lombok.core.AST.Kind; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAST; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.TransformEclipseAST; +import lombok.eclipse.handlers.SetGeneratedByVisitor; +import lombok.patcher.Symbols; +import lombok.permit.Permit; public class PatchDelegate { + private static class ClassScopeEntry { ClassScopeEntry(ClassScope scope) { this.scope = scope; @@ -123,6 +145,14 @@ public class PatchDelegate { public static boolean handleDelegateForType(ClassScope scope) { if (TransformEclipseAST.disableLombok) return false; + + CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext; + if (scope == scope.compilationUnitScope().topLevelTypes[0].scope) { + if (eclipseAvailable) { + EclipseOnlyMethods.cleanupDelegateMethods(cud); + } + } + if (!hasDelegateMarkedFieldsOrMethods(scope.referenceContext)) return false; List<ClassScopeEntry> stack = visited.get(); @@ -149,7 +179,6 @@ public class PatchDelegate { try { TypeDeclaration decl = scope.referenceContext; if (decl != null) { - CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext; EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true); List<BindingTuple> methodsToDelegate = new ArrayList<BindingTuple>(); fillMethodBindingsForFields(cud, scope, methodsToDelegate); @@ -168,6 +197,9 @@ public class PatchDelegate { } } finally { stack.remove(stack.size() - 1); + if (stack.isEmpty() && eclipseAvailable) { + EclipseOnlyMethods.notifyDelegateMethodsAdded(cud); + } } } @@ -419,6 +451,7 @@ public class PatchDelegate { private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods, DelegateReceiver delegateReceiver) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) typeNode.top().get(); + List<MethodDeclaration> addedMethods = new ArrayList<MethodDeclaration>(); for (BindingTuple pair : methods) { EclipseNode annNode = typeNode.getAst().get(pair.responsible); MethodDeclaration method = createDelegateMethod(pair.fieldName, typeNode, pair, top.compilationResult, annNode, delegateReceiver); @@ -426,8 +459,12 @@ public class PatchDelegate { SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(annNode.get()); method.traverse(visitor, ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, method); + addedMethods.add(method); } } + if (eclipseAvailable) { + EclipseOnlyMethods.collectGeneratedDelegateMethods(top, typeNode, addedMethods); + } } public static void checkConflictOfTypeVarNames(BindingTuple binding, EclipseNode typeNode) throws CantMakeDelegates { @@ -673,20 +710,177 @@ public class PatchDelegate { return method; } + private static boolean eclipseAvailable = true; + static { + try { + CompilationUnit.class.getName(); + } catch (Throwable t) { + eclipseAvailable = false; + } + } + + public static Object[] addGeneratedDelegateMethods(Object[] returnValue, Object javaElement) { + if (Symbols.hasSymbol("lombok.skipdelegates")) return returnValue; + if (!eclipseAvailable) return returnValue; + + return EclipseOnlyMethods.addGeneratedDelegateMethodsToChildren(returnValue, javaElement); + } + + public static class EclipseOnlyMethods { + private static void cleanupDelegateMethods(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + EclipseAugments.CompilationUnit_delegateMethods.clear(compilationUnit); + } + } + + public static void collectGeneratedDelegateMethods(CompilationUnitDeclaration top, EclipseNode typeNode, List<MethodDeclaration> addedMethods) { + String qualifiedName = new String(CharOperation.concatWith(getQualifiedInnerName(typeNode.up(), typeNode.getName().toCharArray()), '$')); + SourceType sourceType = getSourceType(top, qualifiedName); + List<SourceMethod> generatedMethods = getGeneratedMethods(sourceType); + if (generatedMethods == null) return; + + for (MethodDeclaration md : addedMethods) { + generatedMethods.add(DelegateSourceMethod.forMethodDeclaration(sourceType, md)); + } + } + + public static Object[] addGeneratedDelegateMethodsToChildren(Object[] returnValue, Object javaElement) { + List<SourceMethod> delegateMethods = getGeneratedMethods((SourceType) ((SourceTypeElementInfo) javaElement).getHandle()); + if (delegateMethods != null) { + return concat((IJavaElement[]) returnValue, delegateMethods.toArray(new IJavaElement[0]), IJavaElement.class); + } + return returnValue; + } + + private static void notifyDelegateMethodsAdded(CompilationUnitDeclaration cud) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + DeltaProcessor deltaProcessor = JavaModelManager.getJavaModelManager().getDeltaProcessor(); + deltaProcessor.fire(new JavaElementDelta(compilationUnit), ElementChangedEvent.POST_CHANGE); + } + } + + private static CompilationUnit getCompilationUnit(Object iCompilationUnit) { + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + return compilationUnit.originalFromClone(); + } + return null; + } + + private static CompilationUnit getCompilationUnit(CompilationUnitDeclaration cud) { + return getCompilationUnit(cud.compilationResult.compilationUnit); + } + + private static final class DelegateSourceMethod extends SourceMethod { + private DelegateSourceMethodInfo sourceMethodInfo; + + private static DelegateSourceMethod forMethodDeclaration(JavaElement parent, MethodDeclaration method) { + Argument[] arguments = method.arguments != null ? method.arguments : new Argument[0]; + String[] parameterTypes = new String[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + parameterTypes[i] = Signature.createTypeSignature(CharOperation.concatWith(arguments[i].type.getParameterizedTypeName(), '.'), false); + } + return new DelegateSourceMethod(parent, new String(method.selector), parameterTypes, method); + } + + private DelegateSourceMethod(JavaElement parent, String name, String[] parameterTypes, MethodDeclaration md) { + super(parent, name, parameterTypes); + sourceMethodInfo = new DelegateSourceMethodInfo(this, md); + } + + @Override public Object getElementInfo() throws JavaModelException { + return sourceMethodInfo; + } + + /** + * Disable refactoring for delegate methods + */ + @Override public boolean isReadOnly() { + return true; + } + + /** + * This is required to prevent duplicate entries in the outline + */ + @Override public boolean equals(Object o) { + return this == o; + } + + public static final class DelegateSourceMethodInfo extends SourceMethodInfo { + DelegateSourceMethodInfo(DelegateSourceMethod delegateSourceMethod, MethodDeclaration md) { + int pS = md.sourceStart; + int pE = md.sourceEnd; + + Argument[] methodArguments = md.arguments != null ? md.arguments : new Argument[0]; + char[][] argumentNames = new char[methodArguments.length][]; + arguments = new ILocalVariable[methodArguments.length]; + for (int i = 0; i < methodArguments.length; i++) { + Argument argument = methodArguments[i]; + argumentNames[i] = argument.name; + arguments[i] = new LocalVariable(delegateSourceMethod, new String(argument.name), pS, pE, pS, pS, delegateSourceMethod.getParameterTypes()[i], argument.annotations, argument.modifiers, true); + } + setArgumentNames(argumentNames); + + setSourceRangeStart(pS); + setSourceRangeEnd(pE); + setNameSourceStart(pS); + setNameSourceEnd(pE); + + setExceptionTypeNames(CharOperation.NO_CHAR_CHAR); + setReturnType(md.returnType == null ? new char[]{'v', 'o','i', 'd'} : CharOperation.concatWith(md.returnType.getParameterizedTypeName(), '.')); + setFlags(md.modifiers); + } + } + } + + private static List<SourceMethod> getGeneratedMethods(SourceType sourceType) { + if (sourceType != null) { + CompilationUnit compilationUnit = getCompilationUnit(sourceType.getCompilationUnit()); + if (compilationUnit != null) { + ConcurrentMap<String, List<SourceMethod>> map = EclipseAugments.CompilationUnit_delegateMethods.setIfAbsent(compilationUnit, new ConcurrentHashMap<String, List<SourceMethod>>()); + List<SourceMethod> newList = new ArrayList<SourceMethod>(); + List<SourceMethod> oldList = map.putIfAbsent(sourceType.getTypeQualifiedName(), newList); + return oldList != null ? oldList : newList; + } + } + return null; + } + + private static SourceType getSourceType(CompilationUnitDeclaration cud, String typeName) { + CompilationUnit compilationUnit = getCompilationUnit(cud); + if (compilationUnit != null) { + try { + for (IType type : compilationUnit.getAllTypes()) { + if (type instanceof SourceType && type.getTypeQualifiedName().equals(typeName)) { + return (SourceType) type; + } + } + } catch (JavaModelException e) { + // Ignore + } + } + return null; + } + } + private static final class Reflection { public static final Method classScopeBuildFieldsAndMethodsMethod; - + public static final Throwable initCause; static { Method m = null; + Throwable c = null; try { - m = ClassScope.class.getDeclaredMethod("buildFieldsAndMethods"); - m.setAccessible(true); + m = Permit.getMethod(ClassScope.class, "buildFieldsAndMethods"); } catch (Throwable t) { + c = t; // That's problematic, but as long as no local classes are used we don't actually need it. // Better fail on local classes than crash altogether. } classScopeBuildFieldsAndMethodsMethod = m; + initCause = c; } } @@ -723,16 +917,14 @@ public class PatchDelegate { ClassScope cs = ((SourceTypeBinding)inner).scope; if (cs != null) { try { - Reflection.classScopeBuildFieldsAndMethodsMethod.invoke(cs); + Permit.invoke(Reflection.initCause, Reflection.classScopeBuildFieldsAndMethodsMethod, cs); } catch (Exception e) { // See 'Reflection' class for why we ignore this exception. } } } - if (!(binding instanceof ReferenceBinding)) { - return; - } + if (!(binding instanceof ReferenceBinding)) return; ReferenceBinding rb = (ReferenceBinding) binding; MethodBinding[] availableMethods = rb.availableMethods(); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java index 49083df0..01e4bb18 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegatePortal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,52 +21,54 @@ */ package lombok.eclipse.agent; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import lombok.Lombok; +import lombok.permit.Permit; public class PatchDelegatePortal { static final String CLASS_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.ClassScope"; + static final String I_JAVA_ELEMENT_ARRAY = "[Lorg.eclipse.jdt.core.IJavaElement;"; + static final String SOURCE_TYPE_ELEMENT_INFO = "org.eclipse.jdt.internal.core.SourceTypeElementInfo"; public static boolean handleDelegateForType(Object classScope) { - try { - return (Boolean) Reflection.handleDelegateForType.invoke(null, classScope); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return false; - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e.getCause()); - } catch (NullPointerException e) { - if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { - e.initCause(Reflection.problem); - throw e; - } - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return false; - } + Boolean v = (Boolean) Permit.invokeSneaky(Reflection.problemHandleDelegate, Reflection.handleDelegateForType, null, classScope); + if (v == null) return false; + return v.booleanValue(); + } + + public static Object[] addGeneratedDelegateMethods(Object returnValue, Object javaElement) { + return (Object[]) Permit.invokeSneaky(Reflection.problemAddGeneratedDelegateMethods, Reflection.addGeneratedDelegateMethods, null, returnValue, javaElement); } private static final class Reflection { public static final Method handleDelegateForType; - public static final Throwable problem; + public static final Method addGeneratedDelegateMethods; + public static final Throwable problemHandleDelegate; + public static final Throwable problemAddGeneratedDelegateMethods; static { - Method m = null; - Throwable problem_ = null; + Method m = null, n = null; + Throwable problemHandleDelegate_ = null; + Throwable problemAddGeneratedDelegateMethods_ = null; try { - m = PatchDelegate.class.getMethod("handleDelegateForType", Class.forName(CLASS_SCOPE)); + m = Permit.getMethod(PatchDelegate.class, "handleDelegateForType", Class.forName(CLASS_SCOPE)); } catch (Throwable t) { // That's problematic, but as long as no local classes are used we don't actually need it. // Better fail on local classes than crash altogether. - problem_ = t; + problemHandleDelegate_ = t; } handleDelegateForType = m; - problem = problem_; + problemHandleDelegate = problemHandleDelegate_; + + try { + n = Permit.getMethod(PatchDelegate.class, "addGeneratedDelegateMethods", Object[].class, Object.class); + } catch (Throwable t) { + // That's problematic, but as long as no local classes are used we don't actually need it. + // Better fail on local classes than crash altogether. + problemAddGeneratedDelegateMethods_ = t; + } + addGeneratedDelegateMethods = n; + problemAddGeneratedDelegateMethods = problemAddGeneratedDelegateMethods_; } } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index fcc76059..2e540b5e 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ package lombok.eclipse.agent; import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -54,9 +55,9 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SuperReference; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; @@ -98,13 +99,13 @@ public class PatchExtensionMethod { private static final Method shortMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class); private static final Method longMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class, Scope.class); + private static Throwable initProblem; private static Method getMethod(String name, Class<?>... types) { try { - Method m = ProblemReporter.class.getMethod(name, types); - m.setAccessible(true); - return m; + return Permit.getMethod(ProblemReporter.class, name, types); } catch (Exception e) { + initProblem = e; return null; } } @@ -119,8 +120,9 @@ public class PatchExtensionMethod { static void invoke(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) { if (messageSend != null) { try { - if (shortMethod != null) shortMethod.invoke(problemReporter, messageSend, method); - else if (longMethod != null) longMethod.invoke(problemReporter, messageSend, method, scope); + if (shortMethod != null) Permit.invoke(initProblem, shortMethod, problemReporter, messageSend, method); + else if (longMethod != null) Permit.invoke(initProblem, longMethod, problemReporter, messageSend, method, scope); + else Permit.reportReflectionProblem(initProblem, "method named 'invalidMethod' not found in ProblemReporter.class"); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { @@ -138,6 +140,25 @@ public class PatchExtensionMethod { } } + private static class PostponedNonStaticAccessToStaticMethodError implements PostponedError { + private final ProblemReporter problemReporter; + private ASTNode location; + private MethodBinding method; + private ReferenceContext referenceContext; + + PostponedNonStaticAccessToStaticMethodError(ProblemReporter problemReporter, ASTNode location, MethodBinding method) { + this.problemReporter = problemReporter; + this.location = location; + this.method = method; + this.referenceContext = problemReporter.referenceContext; + } + + public void fire() { + problemReporter.referenceContext = this.referenceContext; + problemReporter.nonStaticAccessToStaticMethod(location, method); + } + } + private static interface PostponedError { public void fire(); } @@ -200,15 +221,12 @@ public class PatchExtensionMethod { TypeBinding receiverType) { List<MethodBinding> extensionMethods = new ArrayList<MethodBinding>(); - CompilationUnitScope cuScope = ((CompilationUnitDeclaration) typeNode.top().get()).scope; for (MethodBinding method : extensionMethodProviderBinding.methods()) { if (!method.isStatic()) continue; if (!method.isPublic()) continue; if (method.parameters == null || method.parameters.length == 0) continue; TypeBinding firstArgType = method.parameters[0]; if (receiverType.isProvablyDistinct(firstArgType) && !receiverType.isCompatibleWith(firstArgType.erasure())) continue; - TypeBinding[] argumentTypes = Arrays.copyOfRange(method.parameters, 1, method.parameters.length); - if ((receiverType instanceof ReferenceBinding) && ((ReferenceBinding) receiverType).getExactMethod(method.selector, argumentTypes, cuScope) != null) continue; extensionMethods.add(method); } return extensionMethods; @@ -228,6 +246,10 @@ public class PatchExtensionMethod { MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, scope)); } + public static void nonStaticAccessToStaticMethod(ProblemReporter problemReporter, ASTNode location, MethodBinding method, MessageSend messageSend) { + MessageSend_postponedErrors.set(messageSend, new PostponedNonStaticAccessToStaticMethodError(problemReporter, location, method)); + } + public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { List<Extension> extensions = new ArrayList<Extension>(); TypeDeclaration decl = scope.classScope().referenceContext; @@ -250,6 +272,14 @@ public class PatchExtensionMethod { Binding binding = ((NameReference)methodCall.receiver).binding; if (binding instanceof TypeBinding) skip = true; } + // It's impossible to resolve the right method without types + if (Reflection.argumentsHaveErrors != null) { + try { + if ((Boolean) Reflection.argumentsHaveErrors.get(methodCall)) skip = true; + } catch (IllegalAccessException ignore) { + // ignore + } + } if (!skip) for (Extension extension : extensions) { if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue; @@ -262,13 +292,31 @@ public class PatchExtensionMethod { List<Expression> arguments = new ArrayList<Expression>(); arguments.add(methodCall.receiver); if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments)); + Expression[] originalArgs = methodCall.arguments; + methodCall.arguments = arguments.toArray(new Expression[0]); + List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>(); for (Expression argument : arguments) { - if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType); - // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' placeholder. THAT is what we ought to be adding here! + TypeBinding argumentType = argument.resolvedType; + if (argumentType == null && Reflection.isFunctionalExpression(argument)) { + argumentType = Reflection.getPolyTypeBinding(argument); + } + if (argumentType == null) { + argumentType = TypeBinding.NULL; + } + argumentTypes.add(argumentType); } - Expression[] originalArgs = methodCall.arguments; - methodCall.arguments = arguments.toArray(new Expression[0]); + + if (methodCall.receiver instanceof MessageSend) { + if (Reflection.inferenceContexts != null) { + try { + Permit.set(Reflection.inferenceContexts, methodCall.receiver, null); + } catch (IllegalAccessException ignore) { + // ignore + } + } + } + MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall); if (fixedBinding instanceof ProblemMethodBinding) { methodCall.arguments = originalArgs; @@ -276,18 +324,33 @@ public class PatchExtensionMethod { PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope); } } else { + // If the extension method uses varargs, the last fixed binding parameter is an array but + // the method arguments are not. Even thought we already know that the method is fine we still + // have to compare each parameter with the type of the array to support autoboxing/unboxing. + boolean isVarargs = fixedBinding.isVarargs(); for (int i = 0, iend = arguments.size(); i < iend; i++) { Expression arg = arguments.get(i); - if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break; - if (arg instanceof MessageSend) { - ((MessageSend) arg).valueCast = arg.resolvedType; + TypeBinding[] parameters = fixedBinding.parameters; + TypeBinding param; + if (isVarargs && i >= parameters.length - 1) { + // Extract the array element type for all vararg arguments + param = parameters[parameters.length - 1].leafComponentType(); + } else { + param = parameters[i]; + } + // Resolve types for lambdas + if (Reflection.isFunctionalExpression(arg)) { + arg.setExpectedType(param); + arg.resolveType(scope); } - if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) { - int id = arg.resolvedType.id; - arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds - } else if (fixedBinding.parameters[i].isBaseType() && !arg.resolvedType.isBaseType()) { - int id = fixedBinding.parameters[i].id; - arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + if (arg.resolvedType != null) { + if (!param.isBaseType() && arg.resolvedType.isBaseType()) { + int id = arg.resolvedType.id; + arg.implicitConversion = TypeIds.BOXING | (id + (id << 4)); // magic see TypeIds + } else if (param.isBaseType() && !arg.resolvedType.isBaseType()) { + int id = parameters[i].id; + arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds + } } } @@ -295,6 +358,7 @@ public class PatchExtensionMethod { methodCall.actualReceiverType = extensionMethod.declaringClass; methodCall.binding = fixedBinding; methodCall.resolvedType = methodCall.binding.returnType; + methodCall.statementEnd = methodCall.sourceEnd; if (Reflection.argumentTypes != null) { try { Reflection.argumentTypes.set(methodCall, argumentTypes.toArray(new TypeBinding[0])); @@ -340,16 +404,42 @@ public class PatchExtensionMethod { } private static final class Reflection { - public static final Field argumentTypes; + public static final Field argumentTypes = Permit.permissiveGetField(MessageSend.class, "argumentTypes"); + public static final Field argumentsHaveErrors = Permit.permissiveGetField(MessageSend.class, "argumentsHaveErrors"); + public static final Field inferenceContexts = Permit.permissiveGetField(MessageSend.class, "inferenceContexts"); + private static final Class<?> functionalExpression; + private static final Constructor<?> polyTypeBindingConstructor; static { - Field a = null; + Class<?> a = null; + Constructor<?> b = null; try { - a = Permit.getField(MessageSend.class, "argumentTypes"); - } catch (Throwable t) { - //ignore - old eclipse versions don't know this one + a = Class.forName("org.eclipse.jdt.internal.compiler.ast.FunctionalExpression"); + } catch (Exception e) { + // Ignore + } + try { + b = Permit.getConstructor(Class.forName("org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding"), Expression.class); + } catch (Exception e) { + // Ignore + } + functionalExpression = a; + polyTypeBindingConstructor = b; + } + + public static boolean isFunctionalExpression(Expression expression) { + if (functionalExpression == null) return false; + return functionalExpression.isInstance(expression); + } + + public static TypeBinding getPolyTypeBinding(Expression expression) { + if (polyTypeBindingConstructor == null) return null; + try { + return (TypeBinding) polyTypeBindingConstructor.newInstance(expression); + } catch (Exception e) { + // Ignore } - argumentTypes = a; + return null; } } } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java index 085c903f..1b94d1b9 100755 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java @@ -65,13 +65,14 @@ public class PatchExtensionMethodCompletionProposal { CompletionProposalCollector completionProposalCollector) { List<IJavaCompletionProposal> proposals = new ArrayList<IJavaCompletionProposal>(Arrays.asList(javaCompletionProposals)); - if (canExtendCodeAssist(proposals)) { - IJavaCompletionProposal firstProposal = proposals.get(0); - int replacementOffset = getReplacementOffset(firstProposal); + if (canExtendCodeAssist()) { for (Extension extension : getExtensionMethods(completionProposalCollector)) { for (MethodBinding method : extension.extensionMethods) { - ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset); - copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal); + if (!isMatchingProposal(method, completionProposalCollector)) { + continue; + } + ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(0); + copyNameLookupAndCompletionEngine(completionProposalCollector, newProposal); ASTNode node = getAssistNode(completionProposalCollector); newProposal.setMethodBinding(method, node); createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals); @@ -81,7 +82,6 @@ public class PatchExtensionMethodCompletionProposal { return proposals.toArray(new IJavaCompletionProposal[0]); } - private static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) { List<Extension> extensions = new ArrayList<Extension>(); ClassScope classScope = getClassScope(completionProposalCollector); @@ -96,6 +96,17 @@ public class PatchExtensionMethodCompletionProposal { return extensions; } + private static boolean isMatchingProposal(MethodBinding method, CompletionProposalCollector completionProposalCollector) { + try { + InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); + String searchToken = new String(context.getToken()); + String extensionMethodName = new String(method.selector); + return extensionMethodName.contains(searchToken); + } catch (IllegalAccessException e) { + return true; + } + } + static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) { TypeBinding firstParameterType = null; ASTNode node = getAssistNode(completionProposalCollector); @@ -149,8 +160,7 @@ public class PatchExtensionMethodCompletionProposal { return scope; } - private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal, - InternalCompletionProposal newProposal) { + private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, InternalCompletionProposal newProposal) { try { InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector); @@ -173,17 +183,10 @@ public class PatchExtensionMethodCompletionProposal { } } - private static boolean canExtendCodeAssist(List<IJavaCompletionProposal> proposals) { - return !proposals.isEmpty() && Reflection.isComplete(); - } - - private static int getReplacementOffset(Object proposal) { - try { - return Reflection.replacementOffsetField.getInt(proposal); - } catch (Exception ignore) { - return 0; - } + private static boolean canExtendCodeAssist() { + return Reflection.isComplete(); } + static class Reflection { public static final Field replacementOffsetField; @@ -195,7 +198,7 @@ public class PatchExtensionMethodCompletionProposal { public static final Field completionEngineField; public static final Field nameLookupField; public static final Method createJavaCompletionProposalMethod; - + static { replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset"); contextField = accessField(CompletionProposalCollector.class, "fContext"); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java deleted file mode 100644 index b66bafbb..00000000 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodPortal.java +++ /dev/null @@ -1,103 +0,0 @@ -package lombok.eclipse.agent; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import lombok.Lombok; - -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; - -public class PatchExtensionMethodPortal { - private static final String TYPE_BINDING = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding"; - private static final String TYPE_BINDING_ARRAY = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;"; - private static final String MESSAGE_SEND = "org.eclipse.jdt.internal.compiler.ast.MessageSend"; - private static final String BLOCK_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.BlockScope"; - private static final String METHOD_BINDING = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding"; - private static final String PROBLEM_REPORTER = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter"; - - public static TypeBinding resolveType(Object resolvedType, Object methodCall, Object scope) { - try { - return (TypeBinding) Reflection.resolveType.invoke(null, resolvedType, methodCall, scope); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return (TypeBinding) resolvedType; - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e); - } catch (NullPointerException e) { - if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { - e.initCause(Reflection.problem); - throw e; - } - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - return (TypeBinding)resolvedType; - } - } - - public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) { - try { - Reflection.errorNoMethodFor.invoke(null, problemReporter, messageSend, recType, params); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e.getCause()); - } catch (NullPointerException e) { - if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { - e.initCause(Reflection.problem); - throw e; - } - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } - } - - public static void invalidMethod(Object problemReporter, Object messageSend, Object method) { - try { - Reflection.invalidMethod.invoke(null, problemReporter, messageSend, method); - } catch (NoClassDefFoundError e) { - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } catch (IllegalAccessException e) { - throw Lombok.sneakyThrow(e); - } catch (InvocationTargetException e) { - throw Lombok.sneakyThrow(e.getCause()); - } catch (NullPointerException e) { - if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) { - e.initCause(Reflection.problem); - throw e; - } - //ignore, we don't have access to the correct ECJ classes, so lombok can't possibly - //do anything useful here. - } - } - - private static final class Reflection { - public static final Method resolveType, errorNoMethodFor, invalidMethod; - public static final Throwable problem; - - static { - Method m = null, n = null, o = null; - Throwable problem_ = null; - try { - m = PatchExtensionMethod.class.getMethod("resolveType", Class.forName(TYPE_BINDING), Class.forName(MESSAGE_SEND), Class.forName(BLOCK_SCOPE)); - n = PatchExtensionMethod.class.getMethod("errorNoMethodFor", Class.forName(PROBLEM_REPORTER), - Class.forName(MESSAGE_SEND), Class.forName(TYPE_BINDING), Class.forName(TYPE_BINDING_ARRAY)); - o = PatchExtensionMethod.class.getMethod("invalidMethod", Class.forName(PROBLEM_REPORTER), Class.forName(MESSAGE_SEND), Class.forName(METHOD_BINDING)); - } catch (Throwable t) { - // That's problematic, but as long as no local classes are used we don't actually need it. - // Better fail on local classes than crash altogether. - problem_ = t; - } - resolveType = m; - errorNoMethodFor = n; - invalidMethod = o; - problem = problem_; - } - } -} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java new file mode 100644 index 00000000..5b34917e --- /dev/null +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchJavadoc.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Project Lombok Authors. + * + * 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. + */ +package lombok.eclipse.agent; + +import static lombok.eclipse.EcjAugments.CompilationUnit_javadoc; + +import java.lang.reflect.Method; +import java.util.Map; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; + +import lombok.eclipse.handlers.EclipseHandlerUtil; +import lombok.permit.Permit; + +public class PatchJavadoc { + + public static String getHTMLContentFromSource(String original, Object member) { + if (original != null) { + return original; + } + + if (member instanceof SourceMethod) { + SourceMethod sourceMethod = (SourceMethod) member; + ICompilationUnit iCompilationUnit = sourceMethod.getCompilationUnit(); + if (iCompilationUnit instanceof CompilationUnit) { + CompilationUnit compilationUnit = (CompilationUnit) iCompilationUnit; + Map<String, String> docs = CompilationUnit_javadoc.get(compilationUnit); + if (docs == null) return null; + + String signature = Signature.getSignature(sourceMethod); + String rawJavadoc = docs.get(signature); + if (rawJavadoc == null) return null; + + return Reflection.javadoc2HTML((IMember) member, (IJavaElement) member, rawJavadoc); + } + } + + return null; + } + + public static StringBuffer printMethod(AbstractMethodDeclaration methodDeclaration, Integer tab, StringBuffer output, TypeDeclaration type) { + Map<String, String> docs = CompilationUnit_javadoc.get(methodDeclaration.compilationResult.compilationUnit); + if (docs != null) { + String signature = EclipseHandlerUtil.getSignature(type, methodDeclaration); + String rawJavadoc = docs.get(signature); + if (rawJavadoc != null) { + for (String line : rawJavadoc.split("\r?\n")) { + ASTNode.printIndent(tab, output).append(line).append("\n"); + } + } + } + return methodDeclaration.print(tab, output); + } + + private static class Signature { + static final String getSignature(SourceMethod sourceMethod) { + StringBuilder sb = new StringBuilder(); + sb.append(sourceMethod.getParent().getElementName()); + sb.append("."); + sb.append(sourceMethod.getElementName()); + sb.append("("); + for (String type : sourceMethod.getParameterTypes()) { + sb.append(org.eclipse.jdt.core.Signature.toString(type)); + } + sb.append(")"); + + return sb.toString(); + } + } + + /** + * The method <code>javadoc2HTML</code> changed 2014-12 to accept an + * additional IJavaElement parameter. To support older versions, try to + * find that one too. + */ + private static class Reflection { + private static final Method javadoc2HTML; + private static final Method oldJavadoc2HTML; + static { + Method a = null, b = null; + + try { + a = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, IJavaElement.class, String.class); + } catch (Throwable t) {} + try { + b = Permit.getMethod(JavadocContentAccess2.class, "javadoc2HTML", IMember.class, String.class); + } catch (Throwable t) {} + + javadoc2HTML = a; + oldJavadoc2HTML = b; + } + + private static String javadoc2HTML(IMember member, IJavaElement element, String rawJavadoc) { + if (javadoc2HTML != null) { + try { + return (String) javadoc2HTML.invoke(null, member, element, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + if (oldJavadoc2HTML != null) { + try { + return (String) oldJavadoc2HTML.invoke(null, member, rawJavadoc); + } catch (Throwable t) { + return null; + } + } + return null; + } + } +} diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index f22e78a8..9663f364 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2019 The Project Lombok Authors. + * Copyright (C) 2010-2021 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -68,32 +68,7 @@ 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. - - public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) { - if (expr.resolvedType != null) return expr.resolvedType; - try { - return expr.resolveType(scope); - } catch (NullPointerException e) { - return null; - } catch (ArrayIndexOutOfBoundsException e) { - // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. - return null; - } - } - - public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) { - if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType; - try { - return expr.resolveType(scope); - } catch (NullPointerException e) { - return null; - } catch (ArrayIndexOutOfBoundsException e) { - // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments. - return null; - } - } + // Also patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one. public static boolean matches(String key, char[] array) { if (array == null || key.length() != array.length) return false; |