Skip to content

Commit

Permalink
incremental annotation processing environment (IDEA-229864, IDEA-2507…
Browse files Browse the repository at this point in the history
…18, IDEA-250670)

GitOrigin-RevId: 2ee7f2b843f77d0f00e39584e0f64fc2a1df4b91
  • Loading branch information
Eugene Zhuravlev authored and intellij-monorepo-bot committed Sep 25, 2020
1 parent 6fbd744 commit d6f2625
Show file tree
Hide file tree
Showing 21 changed files with 952 additions and 339 deletions.
2 changes: 1 addition & 1 deletion jps/jps-builders-6/proto/javac_remote_proto.proto
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ message Message {
optional string output_root = 3;
optional string relative_path = 4;
optional string class_name = 5;
optional string source_uri = 6;
repeated string source_uri = 6;
optional bytes content = 7;
optional string location = 8;
}
Expand Down
242 changes: 242 additions & 0 deletions jps/jps-builders-6/src/org/jetbrains/jps/javac/APIWrappers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.javac;

import com.intellij.openapi.util.Pair;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

public class APIWrappers {

public static Processor newProcessorWrapper(final Processor delegate, JpsJavacFileManager fileManager) {
return wrap(Processor.class, new ProcessorWrapper(delegate, fileManager), delegate);
}

abstract static class DynamicWrapper<T> {

private final T myDelegate;

DynamicWrapper(T delegate) {
myDelegate = delegate;
}

protected final T getDelegate() {
return myDelegate;
}
}

static class ProcessorWrapper extends DynamicWrapper<Processor> {
private final JpsJavacFileManager myFileManager;

ProcessorWrapper(Processor delegate, JpsJavacFileManager fileManager) {
super(delegate);
myFileManager = fileManager;
}

public void init(ProcessingEnvironment processingEnv) {
getDelegate().init(wrap(ProcessingEnvironment.class, new ProcessingEnvironmentWrapper(processingEnv, myFileManager), processingEnv));
}

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return getDelegate().process(annotations, roundEnv);
}
}

static class ProcessingEnvironmentWrapper extends DynamicWrapper<ProcessingEnvironment> {
private final JpsJavacFileManager myFileManager;
private Filer myFilerImpl;

ProcessingEnvironmentWrapper(ProcessingEnvironment delegate, JpsJavacFileManager fileManager) {
super(delegate);
myFileManager = fileManager;
}

public Filer getFiler() {
Filer impl = myFilerImpl;
if (impl == null) {
final Filer delegateFiler = getDelegate().getFiler();
myFilerImpl = impl = wrap(Filer.class, new FilerWrapper(delegateFiler, myFileManager, getDelegate().getElementUtils()), delegateFiler);
}
return impl;
}
}

static class FilerWrapper extends DynamicWrapper<Filer> implements Filer {
private final JpsJavacFileManager myFileManager;
private final Function<Element, String> convertToClassName;

FilerWrapper(Filer delegate, JpsJavacFileManager fileManager, final Elements elementUtils) {
super(delegate);
myFileManager = fileManager;
convertToClassName = new ClassNameFinder(elementUtils);
}

@Override
public JavaFileObject createSourceFile(CharSequence name, Element... originatingElements) throws IOException {
addMapping(name, Arrays.asList(originatingElements));
return getDelegate().createSourceFile(name, originatingElements);
}

@Override
public JavaFileObject createClassFile(CharSequence name, Element... originatingElements) throws IOException {
addMapping(name, Arrays.asList(originatingElements));
return getDelegate().createClassFile(name, originatingElements);
}

@Override
public FileObject createResource(JavaFileManager.Location location, CharSequence moduleAndPkg, CharSequence relativeName, Element... originatingElements) throws IOException {
if (originatingElements.length > 0) {
final String resourceName;
if (moduleAndPkg == null) {
resourceName = relativeName.toString();
}
else {
StringBuilder buf = new StringBuilder();
for (int i = 0, len = moduleAndPkg.length(); i < len; i++) {
char ch = moduleAndPkg.charAt(i);
if (ch == '/') {
buf.setLength(0); // skip module name
}
else if (ch == '.') {
buf.append('/');
}
else {
buf.append(ch);
}
}
if (buf.length() > 0 && buf.charAt(buf.length() - 1) != '/') {
buf.append('/');
}
resourceName = buf.append(relativeName).toString();
}
// Format: [package-path/]relative-name
// package-path is a package-name where '.' replaced with '/'
addMapping(resourceName, Arrays.asList(originatingElements));
}
return getDelegate().createResource(location, moduleAndPkg, relativeName, originatingElements);
}

@Override
public FileObject getResource(JavaFileManager.Location location, CharSequence moduleAndPkg, CharSequence relativeName) throws IOException {
return getDelegate().getResource(location, moduleAndPkg, relativeName);
}

private void addMapping(CharSequence resourceName, final Collection<? extends Element> elements) {
if (resourceName != null && resourceName.length() > 0 && !elements.isEmpty()) {
myFileManager.addAnnotationProcessingClassMapping(resourceName.toString(), Iterators.filter(Iterators.map(elements, convertToClassName), Iterators.notNullFilter()));
}
}
}

@NotNull
private static <T, W extends DynamicWrapper<T>> T wrap(@NotNull Class<T> ifaceClass, @NotNull final W wrapper, @NotNull final T delegateTo) {
return wrap(ifaceClass, wrapper, DynamicWrapper.class, delegateTo);
}

@NotNull
public static <T> T wrap(@NotNull Class<T> ifaceClass, @NotNull final Object wrapper, @NotNull final Class<?> parentToStopSearchAt, @NotNull final T delegateTo) {
return ifaceClass.cast(Proxy.newProxyInstance(wrapper.getClass().getClassLoader(), new Class[]{ifaceClass}, new InvocationHandler() {
private final Map<Method, Pair<Method, Object>> myCallHandlers = Collections.synchronizedMap(new HashMap<Method, Pair<Method, Object>>());
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
final Pair<Method, Object> call = getCallHandlerMethod(method);
return call.getFirst().invoke(call.getSecond(), args);
}
catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
throw cause != null? cause : e;
}
}

@NotNull
private Pair<Method, Object> getCallHandlerMethod(Method method) {
Pair<Method, Object> pair = myCallHandlers.get(method);
if (pair == null) {
// important: look for implemented methods starting from the actual class
Class<?> aClass = wrapper.getClass();
while (!(parentToStopSearchAt.equals(aClass) || Object.class.equals(aClass))) {
try {
pair = Pair.create(aClass.getDeclaredMethod(method.getName(), method.getParameterTypes()), wrapper);
break;
}
catch (NoSuchMethodException e) {
aClass = aClass.getSuperclass();
}
}
if (pair == null) {
pair = Pair.<Method, Object>create(method, delegateTo);
}
myCallHandlers.put(method, pair);
}
return pair;
}
}));
}

private static class ClassNameFinder implements Function<Element, String> {
private static final Method ourGetQualifiedNameMethod;
private final Name myEmptyName;

static {
Method method = null;
try {
method = Class.forName("javax.lang.model.element.QualifiedNameable").getMethod("getQualifiedName");
}
catch (Throwable ignored) {
}
ourGetQualifiedNameMethod = method;
}

ClassNameFinder(Elements elementUtils) {
myEmptyName = elementUtils.getName("");
}

@Override
public String fun(Element element) {
Name qName = null;
while (element != null) {
if (element instanceof TypeElement) {
qName = ((TypeElement)element).getQualifiedName();
}
else if (element instanceof PackageElement) {
qName = ((PackageElement)element).getQualifiedName();
}
else if (ourGetQualifiedNameMethod != null && ourGetQualifiedNameMethod.getDeclaringClass().isAssignableFrom(element.getClass())) {
try {
qName = (Name)ourGetQualifiedNameMethod.invoke(element);
}
catch (Throwable ignored) {
}
}
if (qName != null) {
if (!qName.equals(myEmptyName)) {
return qName.toString();
}
qName = null;
}
element = element.getEnclosingElement();
}
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.BinaryContent;

import javax.tools.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;

/**
Expand Down Expand Up @@ -93,16 +98,19 @@ public boolean handleMessage(MessageLite message) {

final JavaFileManager.Location location = outputObject.hasLocation()? StandardLocation.locationFor(outputObject.getLocation()) : null;

final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null;
final URI srcUri = sourceUri != null? URI.create(sourceUri) : null;
Collection<URI> sources = new ArrayList<URI>();
for (String uri : outputObject.getSourceUriList()) {
sources.add(URI.create(uri));
}

final OutputFileObject fileObject = new OutputFileObject(
null,
outputRootFile,
outputObject.hasRelativePath()? outputObject.getRelativePath() : null,
new File(outputObject.getFilePath()),
convertKind(kind),
outputObject.hasClassName()? outputObject.getClassName() : null,
srcUri,
sources,
myEncodingName, fileObjectContent, location
);

Expand Down
66 changes: 60 additions & 6 deletions jps/jps-builders-6/src/org/jetbrains/jps/javac/Iterators.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,30 @@
import com.intellij.util.Functions;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.*;

public class Iterators {

private static <T> boolean isEmpty(Iterable<T> iterable) {
return iterable == Collections.emptyList() || iterable == Collections.emptySet() || iterable == Collections.emptyMap();
@SuppressWarnings("rawtypes")
private static final BooleanFunction NOT_NULL_FILTER = new BooleanFunction() {
@Override
public boolean fun(Object s) {
return s != null;
}
};

public static <T> boolean isEmpty(Iterable<T> iterable) {
return iterable == null || (iterable instanceof Collection && ((Collection<?>)iterable).isEmpty());
}

@SuppressWarnings("unchecked")
public static <T> Iterable<T> flat(final Iterable<? extends T> first, final Iterable<? extends T> second) {
if (isEmpty(first)) {
return isEmpty(second)? Collections.<T>emptyList() : (Iterable<T>)second;
}
if (isEmpty(second)) {
return (Iterable<T>)first;
}
return new Iterable<T>() {
@Override
@NotNull
Expand Down Expand Up @@ -210,6 +222,48 @@ private void findNext() {
};
}

public static <T> Iterable<T> filterWithOrder(final Iterable<? extends T> from, final Iterable<BooleanFunction<? super T>> predicates) {
return isEmpty(predicates) || isEmpty(from)? Collections.<T>emptyList() : new Iterable<T>() {
@NotNull
@Override
public Iterator<T> iterator() {
return filterWithOrder(from.iterator(), predicates.iterator());
}
};
}

public static <T> Iterator<T> filterWithOrder(final Iterator<? extends T> from, final Iterator<BooleanFunction<? super T>> predicates) {
return flat(map(predicates, new Function<BooleanFunction<? super T>, Iterator<T>>() {
final List<T> buffer = new LinkedList<T>();
@Override
public Iterator<T> fun(BooleanFunction<? super T> pred) {
if (!buffer.isEmpty()) {
for (Iterator<T> it = buffer.iterator(); it.hasNext(); ) {
final T elem = it.next();
if (pred.fun(elem)) {
it.remove();
return asIterator(elem);
}
}
}
while(from.hasNext()) {
final T elem = from.next();
if (pred.fun(elem)) {
return asIterator(elem);
}
buffer.add(elem);
}
buffer.clear();
return Collections.<T>emptyList().iterator();
}
}));
}

@SuppressWarnings("unchecked")
public static <T> BooleanFunction<? super T> notNullFilter() {
return (BooleanFunction<T>)NOT_NULL_FILTER;
}

private static abstract class BaseIterator<T> implements Iterator<T> {
@Override
public void remove() {
Expand Down

3 comments on commit d6f2625

@jbeckers
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eugenezh This change means an annotation processor in IntelliJ can no longer create a com.sun.source.util.Trees object, since Trees.instance() checks for class name "com.sun.tools.javac.processing.JavacProcessingEnvironment" (but finds some Proxy$35-thing).

I'll file a youtrack.

@oday-xiexin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After upgrading idea2020.3, I found mapstruct 1.2.0.final doesn't work properly

@oday-xiexin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eugenezh此更改意味着IntelliJ中的注释处理器无法再创建com.sun.source.util.Trees对象,因为Trees.instance()会检查类名“ com.sun.tools.javac.processing.JavacProcessingEnvironment”(但找到一些Proxy $ 35的东西)。

我将提交一个youtrack。

After upgrading idea2020.3, I found mapstruct 1.2.0.final doesn't work properly

Please sign in to comment.