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