Skip to content

Commit

Permalink
Implement ProxyNonConstantType (#1799)
Browse files Browse the repository at this point in the history
Implement ProxyNonConstantType
  • Loading branch information
carterkozak authored Jun 22, 2021
1 parent 88d4049 commit 86c9c9c
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* (c) Copyright 2020 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 com.google.auto.service.AutoService;
import com.google.common.reflect.Reflection;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.method.MethodMatchers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewArrayTree;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

@AutoService(BugChecker.class)
@BugPattern(
name = "ProxyNonConstantType",
link = "https://github.com/palantir/gradle-baseline#baseline-error-prone-checks",
linkType = LinkType.CUSTOM,
severity = SeverityLevel.SUGGESTION,
summary = "Proxy instances should be created using constant types known at compile time to allow native-image "
+ "behavior to match hotspot. Methods which build proxies should take a "
+ "`Function<InvocationHandler, ? extends T>` instead of arbitrary class references. "
+ "The proxy annotation processor can make this process much easier: "
+ "https://github.com/palantir/proxy-processor\n"
+ "See https://www.graalvm.org/reference-manual/native-image/DynamicProxy/#automatic-detection")
public final class ProxyNonConstantType extends BugChecker implements BugChecker.MethodInvocationTreeMatcher {

private static final Matcher<ExpressionTree> NEW_PROXY_INSTANCE_MATCHER =
MethodMatchers.staticMethod().onClass(Proxy.class.getName()).named("newProxyInstance");

private static final Matcher<ExpressionTree> REFLECTION_NEW_PROXY = MethodMatchers.staticMethod()
.onClass(Reflection.class.getName())
.named("newProxy")
.withParameters(Class.class.getName(), InvocationHandler.class.getName());

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (REFLECTION_NEW_PROXY.matches(tree, state)) {
return describeMatch(tree);
}
if (NEW_PROXY_INSTANCE_MATCHER.matches(tree, state)) {
ExpressionTree interfaces = tree.getArguments().get(1);
if (interfaces instanceof NewArrayTree) {
NewArrayTree newArrayTree = (NewArrayTree) interfaces;
for (ExpressionTree element : newArrayTree.getInitializers()) {
if (!isDirectClassAccess(element)) {
return describeMatch(interfaces);
}
}
}
}

return Description.NO_MATCH;
}

private static boolean isDirectClassAccess(ExpressionTree expressionTree) {
return expressionTree instanceof MemberSelectTree
&& ((MemberSelectTree) expressionTree).getIdentifier().contentEquals("class");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* (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 com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;

class ProxyNonConstantTypeTest {

@Test
void testGuavaReflectionNewProxy() {
helper().addSourceLines(
"Test.java",
"import com.google.common.reflect.Reflection;",
"class Test {",
" void f() {",
" // BUG: Diagnostic contains: proxy",
" Reflection.newProxy(Test.class, null);",
" }",
"}")
.doTest();
}

@Test
void testConstantInterfacesInline() {
helper().addSourceLines(
"Test.java",
"import java.lang.reflect.Proxy;",
"class Test {",
" void f() {",
" Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Test.class}, null);",
" }",
"}")
.doTest();
}

@Test
void testConstantInterfacesDynamic() {
helper().addSourceLines(
"Test.java",
"import java.lang.reflect.Proxy;",
"class Test {",
" void f(Class<?> iface) {",
" // BUG: Diagnostic contains: proxy",
" Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{iface}, null);",
" }",
"}")
.doTest();
}

private CompilationTestHelper helper() {
return CompilationTestHelper.newInstance(ProxyNonConstantType.class, getClass());
}
}
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-1799.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: Implement ProxyNonConstantType
links:
- https://github.com/palantir/gradle-baseline/pull/1799

0 comments on commit 86c9c9c

Please sign in to comment.