Skip to content

Suppress InaccessibleClassProblem if the class reference is unused in the code attribute#1676

Merged
suztomo merged 17 commits intomasterfrom
unused_in_bytecode
Sep 24, 2020
Merged

Suppress InaccessibleClassProblem if the class reference is unused in the code attribute#1676
suztomo merged 17 commits intomasterfrom
unused_in_bytecode

Conversation

@suztomo
Copy link
Copy Markdown
Contributor

@suztomo suztomo commented Sep 23, 2020

Fixes #1608.

@google-cla google-cla bot added the cla: yes label Sep 23, 2020
@suztomo suztomo changed the title Not to report InaccessibleClassProblem if the class reference is unused in the code attribute Suppress InaccessibleClassProblem if the class reference is unused in the code attribute Sep 23, 2020
Copy link
Copy Markdown
Contributor

@elharo elharo left a comment

Choose a reason for hiding this comment

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

tests seem to fail

@suztomo
Copy link
Copy Markdown
Contributor Author

suztomo commented Sep 23, 2020

2 tests failed
  out of 260
testFindLinkageProblems_shouldStripSourceInnerClasses - com.google.cloud.tools.opensource.classpath.LinkageCheckerTest
org.apache.bcel.classfile.ClassFormatException: The source class in the reference is no longer available in the class path
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindLinkageProblems_shouldStripSourceInnerClasses(LinkageCheckerTest.java:599)
Caused by: java.lang.ClassNotFoundException: Exception while looking for class com.google.foo.Bar$Baz: java.io.IOException: Couldn't find: com/google/foo/Bar$Baz.class
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindLinkageProblems_shouldStripSourceInnerClasses(LinkageCheckerTest.java:599)
Caused by: java.io.IOException: Couldn't find: com/google/foo/Bar$Baz.class
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindLinkageProblems_shouldStripSourceInnerClasses(LinkageCheckerTest.java:599)
testFindClassReferences_privateClass - com.google.cloud.tools.opensource.classpath.LinkageCheckerTest
org.apache.bcel.classfile.ClassFormatException: The source class in the reference is no longer available in the class path
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindClassReferences_privateClass(LinkageCheckerTest.java:569)
Caused by: java.lang.ClassNotFoundException: Exception while looking for class com.google.cloud.tools.opensource.classpath.LinkageCheckerTest: java.io.IOException: Couldn't find: com/google/cloud/tools/opensource/classpath/LinkageCheckerTest.class
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindClassReferences_privateClass(LinkageCheckerTest.java:569)
Caused by: java.io.IOException: Couldn't find: com/google/cloud/tools/opensource/classpath/LinkageCheckerTest.class
	at com.google.cloud.tools.opensource.classpath.LinkageCheckerTest.testFindClassReferences_privateClass(LinkageCheckerTest.java:569)

I'll need to find a good test case of InaccessibleClassProblem

@suztomo
Copy link
Copy Markdown
Contributor Author

suztomo commented Sep 23, 2020

I tried to find an artifact that contains a class that became private from public, and another artifact containing a class that referenced the class. No luck.

Maybe I'll commit a JAR file that contains such bad class files and add instruction to generate one.

@suztomo
Copy link
Copy Markdown
Contributor Author

suztomo commented Sep 24, 2020

@elharo PTAL. I updated the tests.

Comment on lines +659 to +660
// JVM Instruction Set: anewarray, checkcast, instanceof, ldc, ldc2_w, multianewarray,
// and new.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

They cover the ones listed in http://go/jdd-class-reference-validation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think you mean ldc_w, not ldc2_w. ldc2_w is for longs or doubles.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Updated.


SymbolReferences.Builder builder = new SymbolReferences.Builder();
builder.addClassReference(
new ClassFile(dummySource, LinkageCheckerTest.class.getName()),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was using a dummy pair. The dummySource jar does not contain LinkageCheckerTest class.

With this PR, the test cases require real class files that have constant pool section containing the target class.

Comment on lines 656 to 657
Instruction instruction = instructionHandle.getInstruction();
if (instruction instanceof CPInstruction) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@netdpb FYI, BCEL has an abstract class "CPInstruction" that represents an instruction that takes a constant pool index, and we already had this function to determine whether a constant pool index is unused by JVM instructions. (Therefore this function does not list "safe" operations)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's fine, as long as it doesn't list "dangerous" operations either.


if (!isClassAccessibleFrom(targetClass, sourceClassName)) {
return Optional.of(new InaccessibleClassProblem(sourceClassFile, targetClassFile, symbol));
if (classDumper.isUnusedClassSymbolReference(sourceClassName, symbol)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What about:

if (!isClassAccessibleFrom(targetClass, sourceClassName)
    && !classDumper.isUnusedClassSymbolReference(sourceClassName, symbol)) {
  return Optional.of(new InaccessibleClassProblem(sourceClassFile, targetClassFile, symbol));
}
return Optional.empty();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried that and I was confused. Maybe this is more clear:

if (isClassAccessibleFrom(targetClass, sourceClassName)
    || classDumper.isUnusedClassSymbolReference(sourceClassName, symbol)) {
  return Optional.empty();
} else {
  return Optional.of(new InaccessibleClassProblem(sourceClassFile, targetClassFile, symbol));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe, but it does seem like an inversion of the test.

If the class is inaccessible and it's not unused... What if you inverted isUnusedClassSymbolReference?

if (!isClassAccessibleFrom(targetClass, sourceClassname)
    && classDumpter.isClassSymbolReferenceUsed(sourceClassName, symbol)) {
  return Optional.of(…);
}
return Optional.empty();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added isClassSymbolReferenceUsed to ClassDumper.

// io.grpc.grpclb.GrpclbLoadBalancer in grpc-grpclb 0.12.0 had a reference to
// io.grpc.internal.SingleTransportChannel. The SingleTransportChannel became non-public in
// grpc-core 0.15.0.
ClassPathBuilder classPathBuilder = new ClassPathBuilder();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You only use this once. Inline?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated.

}

boolean isClassSymbolReferenceUsed(String sourceClassName, ClassSymbol classSymbol) {
return !isUnusedClassSymbolReference(sourceClassName, classSymbol);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh. Do we need both? I think positive method names are usually better than negative ones. Where else is isUnusedClassSymbolReference used, and can it be switched?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated.

positive method names are usually better than negative ones

Yes, especially when we negate the result.


/**
* Returns true if the class symbol reference is unused in the source class file. It checks
* Returns true if the class symbol reference is used in the source class file. It checks
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

checks the following

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@suztomo suztomo merged commit 703b67c into master Sep 24, 2020
@suztomo suztomo deleted the unused_in_bytecode branch September 24, 2020 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Private subclasses of public classes should not be reported as linkage errors

3 participants