diff --git a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/BaselineErrorProneScope.java b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/BaselineErrorProneScope.java new file mode 100644 index 000000000..d7c46a809 --- /dev/null +++ b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/BaselineErrorProneScope.java @@ -0,0 +1,110 @@ +/* + * (c) Copyright 2021 Palantir Technologies Inc. All rights reserved. + * + * Licensed 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 com.palantir.baseline.errorprone; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.errorprone.util.RuntimeVersion; +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Scope.LookupKind; +import com.sun.tools.javac.code.Symbol; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.function.Predicate; + +/** + * A compatibility wrapper around {@link com.sun.tools.javac.util.Filter}. + * Adapted from {@link com.google.errorprone.util.ErrorProneScope} with additional methods. + * + * Original code from (Apache-2.0 License): + * https://github.com/google/error-prone/blob/ce87ca1c7bb005371837b82ffa69041dd8a356e5/check_api/src/main/java/com/google/errorprone/util/ErrorProneScope.java + * + * TODO(fwindheuser): Delete after upstreaming missing methods into "ErrorProneScope". + */ +@SuppressWarnings("ThrowError") +public final class BaselineErrorProneScope { + + @SuppressWarnings("unchecked") // reflection + public Iterable getSymbols(Predicate predicate, LookupKind lookupKind) { + return (Iterable) invoke(getSymbols, maybeAsFilter(predicate), lookupKind); + } + + private static final Class FILTER_CLASS = getFilterClass(); + + private static Class getFilterClass() { + if (RuntimeVersion.isAtLeast17()) { + return null; + } + try { + return Class.forName("com.sun.tools.javac.util.Filter"); + } catch (ClassNotFoundException e) { + throw new LinkageError(e.getMessage(), e); + } + } + + private static final Method getSymbols = getImpl("getSymbols", Predicate.class, LookupKind.class); + + private static Method getImpl(String name, Class... parameters) { + return FILTER_CLASS != null + ? getMethodOrDie( + Scope.class, + name, + Arrays.stream(parameters) + .map(p -> p.equals(Predicate.class) ? FILTER_CLASS : p) + .toArray(Class[]::new)) + : getMethodOrDie(Scope.class, name, parameters); + } + + private final Scope scope; + + BaselineErrorProneScope(Scope scope) { + this.scope = scope; + } + + private Object invoke(Method method, Object... args) { + try { + return method.invoke(scope, args); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + + @SuppressWarnings("ProxyNonConstantType") + private Object maybeAsFilter(Predicate predicate) { + if (FILTER_CLASS == null) { + return predicate; + } + return Proxy.newProxyInstance( + getClass().getClassLoader(), new Class[] {FILTER_CLASS}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + checkState(method.getName().equals("accepts")); + return predicate.test((Symbol) args[0]); + } + }); + } + + private static Method getMethodOrDie(Class clazz, String name, Class... parameters) { + try { + return clazz.getMethod(name, parameters); + } catch (NoSuchMethodException e) { + throw new LinkageError(e.getMessage(), e); + } + } +} diff --git a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java index 2b6308315..2dabb7f60 100644 --- a/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java +++ b/baseline-error-prone/src/main/java/com/palantir/baseline/errorprone/ClassInitializationDeadlock.java @@ -41,7 +41,6 @@ import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.util.Filter; @AutoService(BugChecker.class) @BugPattern( @@ -130,9 +129,8 @@ private boolean cannotBeInitializedExternally(ClassSymbol newClassSymbol) { if (newClassSymbol.isPrivate()) { return true; } - return !newClassSymbol - .members() - .getSymbols(CanBeExternallyInitializedFilter.INSTANCE, LookupKind.NON_RECURSIVE) + return !new BaselineErrorProneScope(newClassSymbol.members()) + .getSymbols(ClassInitializationDeadlock::canBeExternallyInitialized, LookupKind.NON_RECURSIVE) .iterator() .hasNext(); } @@ -229,28 +227,23 @@ private static boolean isSubtype(Type maybeSubtype, Type baseType, VisitorState && !ASTHelpers.isSameType(maybeSubtype, baseType, state); } - private enum CanBeExternallyInitializedFilter implements Filter { - INSTANCE; - - @Override - public boolean accepts(Symbol symbol) { - switch (symbol.getKind()) { - case FIELD: - // fall through - case METHOD: - if (symbol.isStatic() && !symbol.isPrivate()) { - return true; - } - break; - case CONSTRUCTOR: - if (!symbol.isPrivate()) { - return true; - } - break; - default: - // fall through - } - return false; + private static boolean canBeExternallyInitialized(Symbol symbol) { + switch (symbol.getKind()) { + case FIELD: + // fall through + case METHOD: + if (symbol.isStatic() && !symbol.isPrivate()) { + return true; + } + break; + case CONSTRUCTOR: + if (!symbol.isPrivate()) { + return true; + } + break; + default: + // fall through } + return false; } } diff --git a/changelog/@unreleased/pr-1936.v2.yml b/changelog/@unreleased/pr-1936.v2.yml new file mode 100644 index 000000000..b2cb63a2b --- /dev/null +++ b/changelog/@unreleased/pr-1936.v2.yml @@ -0,0 +1,5 @@ +type: fix +fix: + description: Fix jdk17 incompatibility of ClassInitializationDeadlock + links: + - https://github.com/palantir/gradle-baseline/pull/1936