Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGayan Perera2021-05-18 18:03:45 +0000
committerSarika Sinha2021-06-23 13:36:37 +0000
commit9fc7d75b55627a59392f4124bb4fb5df982ac14a (patch)
treeed4b88dae2d74b0baccbc67bdc98b2e003acf797
parent6663040f653ec737f45831bd6af4c9d33273231c (diff)
downloadeclipse.jdt.core-9fc7d75b55627a59392f4124bb4fb5df982ac14a.tar.gz
eclipse.jdt.core-9fc7d75b55627a59392f4124bb4fb5df982ac14a.tar.xz
eclipse.jdt.core-9fc7d75b55627a59392f4124bb4fb5df982ac14a.zip
Bug 573589 - Add support for lambda evaluation with static imports
When performing lambda expression evaluation where the source file contains static imports, the static imports are added as normal imports into the code snippet. This happens both for source types and binary types with source attached. For binary types with source attached this is fixed in the SourceMapper where the imports are captured. Change-Id: I5ce337191d688138e32f9c43adbbe652851ec90b Signed-off-by: Gayan Perera <gayanper@gmail.com> Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/180745 Tested-by: JDT Bot <jdt-bot@eclipse.org> Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java350
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java4
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/eval/EvaluationContextWrapper.java9
3 files changed, 362 insertions, 1 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java
new file mode 100644
index 0000000000..87c6d95fb3
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java
@@ -0,0 +1,350 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Gayan Perera and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Gayan Perera - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.eval;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
+import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
+import org.eclipse.jdt.core.tests.util.Util;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.core.ClasspathEntry;
+import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jdt.internal.core.eval.EvaluationContextWrapper;
+import org.eclipse.jdt.internal.eval.InstallException;
+
+import junit.framework.Test;
+
+public class EvaluationContextWrapperTest extends EvaluationTest {
+
+ private static final String SOURCE_DIRECTORY = "src";
+ private static final String BIN_DIR = "bin";
+
+ private IJavaProject project;
+
+ static {
+// TESTS_NAMES = new String[] { "testBug573589_StaticImport" };
+ }
+
+ public EvaluationContextWrapperTest(String name) {
+ super(name);
+ }
+
+ public static Test setupSuite(Class clazz) {
+ List<Class<?>> testClasses = new ArrayList<>();
+ testClasses.add(clazz);
+ return buildAllCompliancesTestSuite(clazz, DebugEvaluationSetup.class, testClasses);
+ }
+ public static Test suite() {
+ return setupSuite(testClass());
+ }
+ public static Class<?> testClass() {
+ return EvaluationContextWrapperTest.class;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.project = createProject("EvaluationContextWrapperTest");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ delete(this.project);
+ this.project = null;
+ super.tearDown();
+ }
+
+ public void testBug573589_StaticImport() throws JavaModelException, InstallException {
+ if (this.complianceLevel < ClassFileConstants.JDK1_5) {
+ return;
+ }
+ try {
+ StringBuilder source = new StringBuilder();
+ source.append("import static java.lang.Math.max;\n");
+ source.append("class Bug573589 {\n");
+ source.append(" public int fooMax() {\n");
+ source.append(" return max(10, 11);\n");
+ source.append(" }\n");
+ source.append(" public static void call() {\n");
+ source.append(" new Bug573589().fooMax();\n");
+ source.append(" }\n");
+ source.append("}\n");
+
+ compileAndDeploy15(source.toString(), "Bug573589");
+ refreshProject();
+
+ Optional<IMarker> problem = evaluate("Bug573589", "1+1");
+ assertTrue("Evaluation should not have problems : "
+ .concat(problem.map(p -> p.getAttribute(IMarker.MESSAGE, "")).orElse("")),
+ problem.isEmpty());
+ } finally {
+ removeTempClass("Bug573589");
+ }
+ }
+
+ public void testBug573589_StaticImport_AttachedSource() throws JavaModelException, InstallException {
+ if (this.complianceLevel < ClassFileConstants.JDK1_5) {
+ return;
+ }
+ try {
+ StringBuilder source = new StringBuilder();
+ source.append("import static java.lang.Math.max;\n");
+ source.append("class Bug573589 {\n");
+ source.append(" public int fooMax() {\n");
+ source.append(" return max(10, 11);\n");
+ source.append(" }\n");
+ source.append(" public static void call() {\n");
+ source.append(" new Bug573589().fooMax();\n");
+ source.append(" }\n");
+ source.append("}\n");
+
+ Map<String, String> result = compileAndDeploy15(source.toString(), "Bug573589", "attached");
+ addLibrary(this.project, result.get(BIN_DIR), result.get(SOURCE_DIRECTORY));
+ refreshProject();
+
+ Optional<IMarker> problem = evaluate("Bug573589", "1+1");
+ assertTrue("Evaluation should not have problems : "
+ .concat(problem.map(p -> p.getAttribute(IMarker.MESSAGE, "")).orElse("")),
+ problem.isEmpty());
+ } finally {
+ removeTempClass("Bug573589", "attached");
+ }
+ }
+
+ private void compileAndDeploy15(String source, String className) {
+ compileAndDeploy15(source, className, "");
+ }
+
+ private Map<String, String> compileAndDeploy15(String source, String className, String locationPrefix) {
+ resetEnv(); // needed to reinitialize the caches
+ String srcDir = this.project.getProject().getLocation().toFile().getAbsolutePath() + File.separator + SOURCE_DIRECTORY.concat(locationPrefix);
+ String binDir = this.project.getProject().getLocation().toFile().getAbsolutePath() + File.separator + BIN_DIR.concat(locationPrefix);
+ Map<String, String> result = new HashMap<>();
+ result.put(SOURCE_DIRECTORY, srcDir);
+ result.put(BIN_DIR, binDir);
+
+ File directory = new File(srcDir);
+ if (!directory.exists()) {
+ if (!directory.mkdir()) {
+ System.out.println("Could not create " + srcDir);
+ return result;
+ }
+ }
+
+ String fileName = srcDir + File.separator + className + ".java";
+ try {
+ BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
+ writer.write(source);
+ writer.flush();
+ writer.close();
+ } catch(IOException e) {
+ e.printStackTrace();
+ return result;
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer
+ .append("\"")
+ .append(fileName)
+ .append("\" -d \"")
+ .append(binDir)
+ .append("\" -nowarn -1.5 -g -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append(srcDir)
+ .append("\"");
+ BatchCompiler.compile(buffer.toString(), new PrintWriter(System.out), new PrintWriter(System.err), null/*progress*/);
+ return result;
+ }
+
+ private void refreshProject() {
+ try {
+ this.project.getProject().refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ private void removeTempClass(String className) {
+ removeTempClass(className, "");
+ }
+
+ private void removeTempClass(String className, String locationPrefix) {
+ resetEnv();
+ String srcDir = this.project.getProject().getLocation().toFile().getAbsolutePath() + File.separator + SOURCE_DIRECTORY.concat(locationPrefix);
+ String binDir = this.project.getProject().getLocation().toFile().getAbsolutePath() + File.separator + BIN_DIR.concat(locationPrefix);
+
+ Util.delete(srcDir + File.separator + className + ".java");
+ Util.delete(binDir + File.separator + className + ".class");
+ }
+
+ private Optional<IMarker> evaluate(String declaringTypeName, String snippet) throws InstallException, JavaModelException {
+ IType type = this.project.findType(declaringTypeName);
+ assertNotNull("declaringType is not compiled", type);
+
+ String source = type.getSource();
+ assertNotNull("declaringType source mapper is not ready.", source);
+ assertFalse("declaringType source mapper is not ready.", source.isEmpty());
+
+ IMarker[] problem = new IMarker[1];
+ ICodeSnippetRequestor requestor = new ICodeSnippetRequestor() {
+
+ @Override
+ public void acceptProblem(IMarker problemMarker, String fragmentSource, int fragmentKind) {
+ if (problemMarker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) >= IMarker.SEVERITY_ERROR) {
+ problem[0] = problemMarker;
+ }
+ }
+
+ @Override
+ public boolean acceptClassFiles(byte[][] classFileBytes, String[][] classFileCompoundNames,
+ String codeSnippetClassName) {
+ return true;
+ }
+ };
+
+ EvaluationContextWrapper wrapper = new EvaluationContextWrapper(this.context, (JavaProject) this.project);
+ wrapper.evaluateCodeSnippet(
+ snippet,
+ new String[0],
+ new String[0],
+ new int[0],
+ type,
+ true,
+ false,
+ requestor,
+ null);
+ return Optional.ofNullable(problem[0]);
+ }
+
+ @Override
+ public Map<String, String> getCompilerOptions() {
+ Map<String, String> options = super.getCompilerOptions();
+ options.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE);
+ options.put(CompilerOptions.OPTION_PreserveUnusedLocal, CompilerOptions.PRESERVE);
+ options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_5);
+ options.put(CompilerOptions.OPTION_ReportRawTypeReference, CompilerOptions.IGNORE);
+ options.put(CompilerOptions.OPTION_ReportUnusedImport, CompilerOptions.IGNORE);
+ return options;
+ }
+
+ private IJavaProject createProject(String name) throws Exception {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IProject proj = root.getProject(name);
+ if (proj.exists()) {
+ try {
+ proj.delete(true, true, null);
+ }
+ catch(Exception e) {}
+ }
+ proj = root.getProject(name);
+ proj.create(null);
+ proj.open(null);
+
+ // disable build and set nature
+ IProjectDescription description = proj.getDescription();
+ description.setNatureIds(new String[] {JavaCore.NATURE_ID});
+ description.setBuildConfigs(new String[0]);
+ description.setBuildSpec(new ICommand[0]);
+ proj.setDescription(description, IResource.FORCE, null);
+
+ IFolder binFolder= proj.getFolder(BIN_DIR);
+ if (!binFolder.exists()) {
+ binFolder.create(false, true, null);
+ }
+ IPath outputLocation = binFolder.getFullPath();
+ IJavaProject jproject= JavaCore.create(proj);
+ jproject.setRawClasspath(new IClasspathEntry[0], null);
+ jproject.setOutputLocation(outputLocation, null);
+
+ Map<String, String> map = JavaCore.getOptions();
+ map.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_5);
+ map.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_5);
+ map.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_5);
+ jproject.setOptions(map);
+
+
+ addSourceContainer(jproject, SOURCE_DIRECTORY);
+ for (String path : Util.getJavaClassLibs()) {
+ addLibrary(jproject, path, null);
+ }
+ return jproject;
+ }
+
+ private void addSourceContainer(IJavaProject prj, String name) {
+ IFolder folder = prj.getProject().getFolder(name);
+ if(!folder.exists()) {
+ try {
+ folder.create(false, true, null);
+ } catch (CoreException e) {
+ throw new AssertionError(e);
+ }
+ }
+ IClasspathEntry entry = JavaCore.newSourceEntry(prj.getPackageFragmentRoot(folder).getPath());
+ try {
+ prj.setRawClasspath(addToClasspath(prj.getRawClasspath(), entry), null);
+ } catch (JavaModelException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private void addLibrary(IJavaProject prj, String path, String sourcePath) {
+ IClasspathEntry entry = JavaCore.newLibraryEntry(new Path(path), Optional.ofNullable(sourcePath).map(Path::new).orElse(null),
+ Optional.ofNullable(sourcePath).map(p -> new Path(path)).orElse(null));
+ try {
+ prj.setRawClasspath(addToClasspath(prj.getRawClasspath(), entry), null);
+ } catch (JavaModelException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static IClasspathEntry[] addToClasspath(IClasspathEntry[] original, IClasspathEntry add) {
+ IClasspathEntry[] copy = new ClasspathEntry[original.length + 1];
+ System.arraycopy(original, 0, copy, 0, original.length);
+ copy[copy.length - 1] = add;
+ return copy;
+ }
+
+ private static void delete(IJavaProject jproject) throws CoreException {
+ jproject.setRawClasspath(new ClasspathEntry[0], jproject.getProject().getFullPath(), null);
+ jproject.getProject().delete(true, true, null);
+ }
+
+
+}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
index fbf63275c7..3452b8b2cc 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
@@ -58,6 +58,7 @@ import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.codeassist.impl.Keywords;
import org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
import org.eclipse.jdt.internal.compiler.SourceElementParser;
@@ -348,6 +349,9 @@ public class SourceMapper
name[nameLength] = '.';
name[nameLength + 1] = '*';
}
+ if(Flags.isStatic(modifiers)) {
+ name = CharOperation.concatAll(Keywords.STATIC, name, ' ');
+ }
imports[importsCounter++] = name;
this.importsTable.put(this.binaryTypeOrModule, imports);
this.importsCounterTable.put(this.binaryTypeOrModule, Integer.valueOf(importsCounter));
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/eval/EvaluationContextWrapper.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/eval/EvaluationContextWrapper.java
index 1c14be81fe..76281abe0c 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/eval/EvaluationContextWrapper.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/eval/EvaluationContextWrapper.java
@@ -18,10 +18,12 @@ import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.core.eval.IGlobalVariable;
+import org.eclipse.jdt.internal.codeassist.impl.Keywords;
import org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
@@ -209,7 +211,12 @@ public void evaluateCodeSnippet(
if (importsLength != 0) {
char[][] importsNames = new char[importsLength][];
for (int i = 0; i < importsLength; i++) {
- importsNames[i] = imports[i].getElementName().toCharArray();
+ IImportDeclaration importDecl = imports[i];
+ if(Flags.isStatic(importDecl.getFlags())) {
+ importsNames[i] = CharOperation.concatAll(Keywords.STATIC, importDecl.getElementName().toCharArray(), ' ');
+ } else {
+ importsNames[i] = importDecl.getElementName().toCharArray();
+ }
}
this.context.setImports(importsNames);
// turn off import complaints for implicitly added ones

Back to the top