You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jl...@apache.org on 2019/12/28 16:58:24 UTC
[netbeans] branch master updated: If an annotation processor
crashes, just log the error and continue,
rather than crash the whole processing.
This is an automated email from the ASF dual-hosted git repository.
jlahoda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 20cf951 If an annotation processor crashes, just log the error and continue, rather than crash the whole processing.
20cf951 is described below
commit 20cf9519f592e4341c32e00dda5f525c1da8c597
Author: Jan Lahoda <jl...@netbeans.org>
AuthorDate: Sat Dec 28 17:58:08 2019 +0100
If an annotation processor crashes, just log the error and continue, rather than crash the whole processing.
---
.../modules/java/source/indexing/APTUtils.java | 77 +++++-
.../java/source/indexing/CrashingAPTest.java | 258 +++++++++++++++++++++
2 files changed, 334 insertions(+), 1 deletion(-)
diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
index 5da0008..35c7719 100644
--- a/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
+++ b/java/java.source.base/src/org/netbeans/modules/java/source/indexing/APTUtils.java
@@ -18,6 +18,8 @@
*/
package org.netbeans.modules.java.source.indexing;
+import com.sun.tools.javac.util.Abort;
+import com.sun.tools.javac.util.ClientCodeException;
import com.sun.tools.javac.util.Context;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -50,9 +52,18 @@ import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.tools.Diagnostic;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
@@ -77,6 +88,7 @@ import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.RequestProcessor;
import org.openide.util.BaseUtilities;
+import org.openide.util.NbBundle.Messages;
import org.openide.util.Pair;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups;
@@ -359,7 +371,7 @@ public class APTUtils implements ChangeListener, PropertyChangeListener {
Class<?> clazz = Class.forName(name, true, cl);
Object instance = clazz.newInstance();
if (instance instanceof Processor) {
- result.add((Processor) instance);
+ result.add(new ErrorToleratingProcessor((Processor) instance));
}
} catch (ThreadDeath td) {
throw td;
@@ -929,4 +941,67 @@ public class APTUtils implements ChangeListener, PropertyChangeListener {
}
}
}
+
+ private static final class ErrorToleratingProcessor implements Processor {
+
+ private final Processor delegate;
+ private ProcessingEnvironment processingEnv;
+ private boolean valid = true;
+
+ public ErrorToleratingProcessor(Processor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return delegate.getSupportedOptions();
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return delegate.getSupportedAnnotationTypes();
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return delegate.getSupportedSourceVersion();
+ }
+
+ @Override
+ public void init(ProcessingEnvironment processingEnv) {
+ delegate.init(processingEnv);
+ this.processingEnv = processingEnv;
+ }
+
+ @Override
+ @Messages("ERR_ProcessorException=Annotation processor {0} failed with an exception: {1}")
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (!valid) {
+ return false;
+ }
+ try {
+ return delegate.process(annotations, roundEnv);
+ } catch (ClientCodeException | ThreadDeath | Abort err) {
+ valid = false;
+ throw err;
+ } catch (Throwable t) {
+ valid = false;
+ Element el = roundEnv.getRootElements().isEmpty() ? null : roundEnv.getRootElements().iterator().next();
+ StringBuilder exception = new StringBuilder();
+ exception.append(t.getMessage()).append("\n");
+ for (StackTraceElement ste : t.getStackTrace()) {
+ exception.append(ste).append("\n");
+ }
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, Bundle.ERR_ProcessorException(delegate.getClass().getName(), exception.toString()), el);
+ return false;
+ }
+ }
+
+ @Override
+ public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
+ return delegate.getCompletions(element, annotation, member, userText);
+ }
+
+ }
+
}
diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/CrashingAPTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/CrashingAPTest.java
new file mode 100644
index 0000000..39d7f3b
--- /dev/null
+++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/indexing/CrashingAPTest.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.java.source.indexing;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import junit.framework.*;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.TypeElement;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
+import org.netbeans.api.java.queries.AnnotationProcessingQuery;
+import org.netbeans.api.java.queries.AnnotationProcessingQuery.Result;
+import org.netbeans.api.java.queries.AnnotationProcessingQuery.Trigger;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.java.source.TestUtil;
+import org.netbeans.modules.java.source.usages.IndexUtil;
+import org.netbeans.spi.java.classpath.ClassPathProvider;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.netbeans.spi.java.queries.AnnotationProcessingQueryImplementation;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation;
+import org.openide.filesystems.FileLock;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+
+public class CrashingAPTest extends NbTestCase {
+
+ private FileObject src;
+ private FileObject data;
+
+ static {
+ CrashingAPTest.class.getClassLoader().setDefaultAssertionStatus(true);
+ System.setProperty("org.openide.util.Lookup", CrashingAPTest.Lkp.class.getName());
+ Assert.assertEquals(CrashingAPTest.Lkp.class, Lookup.getDefault().getClass());
+ }
+
+ public static class Lkp extends ProxyLookup {
+
+ private static Lkp DEFAULT;
+
+ public Lkp () {
+ Assert.assertNull(DEFAULT);
+ DEFAULT = this;
+ ClassLoader l = Lkp.class.getClassLoader();
+ this.setLookups(
+ new Lookup [] {
+ Lookups.metaInfServices(l),
+ Lookups.singleton(l),
+ Lookups.singleton(ClassPathProviderImpl.getDefault()),
+ Lookups.singleton(SourceLevelQueryImpl.getDefault()),
+ Lookups.singleton(new APQImpl()),
+ });
+ }
+
+ }
+
+
+ public CrashingAPTest(String testName) {
+ super(testName);
+ }
+
+ protected void setUp() throws Exception {
+ clearWorkDir();
+ File workDir = getWorkDir();
+ File cacheFolder = new File (workDir, "cache"); //NOI18N
+ cacheFolder.mkdirs();
+ IndexUtil.setCacheFolder(cacheFolder);
+ FileObject wd = FileUtil.toFileObject(this.getWorkDir());
+ assertNotNull(wd);
+ this.src = wd.createFolder("src");
+ this.data = src.createData("Test","java");
+ FileLock lock = data.lock();
+ try {
+ PrintWriter out = new PrintWriter ( new OutputStreamWriter (data.getOutputStream(lock)));
+ try {
+ out.println ("public class Test {}");
+ } finally {
+ out.close ();
+ }
+ } finally {
+ lock.releaseLock();
+ }
+ ClassPathProviderImpl.getDefault().setClassPaths(TestUtil.getBootClassPath(),
+ ClassPathSupport.createClassPath(new URL[0]),
+ ClassPathSupport.createClassPath(new FileObject[]{this.src}),
+ ClassPathSupport.createClassPath(System.getProperty("java.class.path")));
+ }
+
+ public void testElementHandle() throws Exception {
+ final JavaSource js = JavaSource.forFileObject(data);
+ assertNotNull(js);
+
+ js.runUserActionTask(new Task<CompilationController>() {
+ public void run(CompilationController parameter) throws IOException {
+ parameter.toPhase(Phase.RESOLVED);
+ List<String> messages = parameter.getDiagnostics()
+ .stream()
+ .map(d -> d.getMessage(null))
+ .map(m -> firstLine(m))
+ .collect(Collectors.toList());
+ List<String> expected = Arrays.asList(Bundle.ERR_ProcessorException("org.netbeans.modules.java.source.indexing.CrashingAPTest$TestAP", "Crash"));
+ assertEquals(expected, messages);
+ }
+ },true);
+ }
+
+ private String firstLine(String m) {
+ int newLine = m.indexOf('\n');
+ if (newLine == (-1)) return m;
+ return m.substring(0, newLine);
+ }
+
+ private static class ClassPathProviderImpl implements ClassPathProvider {
+
+ private static ClassPathProviderImpl instance;
+
+ private ClassPath compile;
+ private ClassPath boot;
+ private ClassPath src;
+ private ClassPath processorPath;
+
+ private ClassPathProviderImpl () {
+
+ }
+
+ public synchronized ClassPath findClassPath(FileObject file, String type) {
+ if (ClassPath.COMPILE.equals(type)) {
+ return compile;
+ }
+ else if (ClassPath.BOOT.equals(type)) {
+ return boot;
+ }
+ else if (ClassPath.SOURCE.equals(type)) {
+ return src;
+ }
+ else if (JavaClassPathConstants.PROCESSOR_PATH.equals(type)) {
+ return processorPath;
+ }
+ else {
+ return null;
+ }
+ }
+
+ public synchronized void setClassPaths (ClassPath boot, ClassPath compile, ClassPath src, ClassPath processorPath) {
+ this.boot = boot;
+ this.compile = compile;
+ this.src = src;
+ this.processorPath = processorPath;
+ }
+
+ public static synchronized ClassPathProviderImpl getDefault () {
+ if (instance == null) {
+ instance = new ClassPathProviderImpl ();
+ }
+ return instance;
+ }
+ }
+
+ private static class SourceLevelQueryImpl implements SourceLevelQueryImplementation {
+
+ private static SourceLevelQueryImpl instance;
+
+ private SourceLevelQueryImpl() {}
+
+ @Override
+ public String getSourceLevel(FileObject javaFile) {
+ return "8";
+ }
+
+ public static synchronized SourceLevelQueryImpl getDefault () {
+ if (instance == null) {
+ instance = new SourceLevelQueryImpl();
+ }
+ return instance;
+ }
+ }
+
+ private static class APQImpl implements AnnotationProcessingQueryImplementation {
+
+ private final Result result = new Result() {
+ public @NonNull Set<? extends Trigger> annotationProcessingEnabled() {
+ return EnumSet.allOf(Trigger.class);
+ }
+
+ public @CheckForNull Iterable<? extends String> annotationProcessorsToRun() {
+ return Arrays.asList(TestAP.class.getName());
+ }
+
+ public @CheckForNull URL sourceOutputDirectory() {
+ return null;
+ }
+
+ public @NonNull Map<? extends String, ? extends String> processorOptions() {
+ return Collections.emptyMap();
+ }
+
+ public void addChangeListener(@NonNull ChangeListener l) {}
+
+ public void removeChangeListener(@NonNull ChangeListener l) {}
+ };
+ @Override
+ public AnnotationProcessingQuery.Result getAnnotationProcessingOptions(FileObject file) {
+ return result;
+ }
+
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class TestAP extends AbstractProcessor {
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ throw new IllegalStateException("Crash");
+ }
+ }
+
+ static {
+ System.setProperty("SourcePath.no.source.filter", "true");
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists