-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add referenced class objects to JavaClass
To detect usages of class objects we need to look which classes are referenced in the constant pool. So far the following usage of `SomeClass` would not have been detected by ArchUnit: ``` class Example { private Map<Class<?>, Value> map = Map.of(SomeClass.class, someValue); } ``` In other words, pure usages of `Foo.class` could not be detected, because there was no "access" to `Foo` in the bytecode. However, for each such occurrence the class would actually be present in the constant pool of the referring class. While ASM does not allow direct access to the constant pool, it does allow us to hook into `ldc` instructions, i.e. load constant instructions in the bytecode, that will in turn reference the respective class. The way to detect this is principally an `instanceof` check for `org.objectweb.asm.Type` in `void visitLdcInsn(Object value)`. As far as I could see, any reference of another class as a constant would cause this method to be called with the respective ASM `Type` object. It would actually be possible to import a lot more constants here. I have looked a little into adding all supported constant types, so it would e.g. be possible to have assertions on `String` values, but then decided to let it go for now. Strings would still be simple, but as soon as `Integer` comes into play (which could also be imported), there are a lot of internal optimizations by the JVM (e.g. `iconst_1`, ...), which makes it hard to do this in a consistent way. I think the most valuable feature by far is to detect constant loads of classes (since those cause dependencies), so I decided to keep it simple for now. Signed-off-by: Peter Gafert <[email protected]>
- Loading branch information
1 parent
1b00986
commit 33b1e45
Showing
13 changed files
with
363 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
archunit/src/main/java/com/tngtech/archunit/core/domain/ReferencedClassObject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* Copyright 2014-2021 TNG Technology Consulting GmbH | ||
* | ||
* 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.tngtech.archunit.core.domain; | ||
|
||
import com.tngtech.archunit.PublicAPI; | ||
import com.tngtech.archunit.base.ChainableFunction; | ||
import com.tngtech.archunit.core.domain.properties.HasOwner; | ||
import com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation; | ||
import com.tngtech.archunit.core.domain.properties.HasType; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS; | ||
|
||
@PublicAPI(usage = ACCESS) | ||
public final class ReferencedClassObject implements HasType, HasOwner<JavaCodeUnit>, HasSourceCodeLocation { | ||
private final JavaCodeUnit owner; | ||
private final JavaClass value; | ||
private final int lineNumber; | ||
private final SourceCodeLocation sourceCodeLocation; | ||
|
||
private ReferencedClassObject(JavaCodeUnit owner, JavaClass value, int lineNumber) { | ||
this.owner = checkNotNull(owner); | ||
this.value = checkNotNull(value); | ||
this.lineNumber = lineNumber; | ||
sourceCodeLocation = SourceCodeLocation.of(owner.getOwner(), lineNumber); | ||
} | ||
|
||
@Override | ||
@PublicAPI(usage = ACCESS) | ||
public JavaType getType() { | ||
return getRawType(); | ||
} | ||
|
||
@Override | ||
@PublicAPI(usage = ACCESS) | ||
public JavaClass getRawType() { | ||
return value; | ||
} | ||
|
||
@Override | ||
@PublicAPI(usage = ACCESS) | ||
public JavaCodeUnit getOwner() { | ||
return owner; | ||
} | ||
|
||
@PublicAPI(usage = ACCESS) | ||
public JavaClass getValue() { | ||
return value; | ||
} | ||
|
||
@PublicAPI(usage = ACCESS) | ||
public int getLineNumber() { | ||
return lineNumber; | ||
} | ||
|
||
@Override | ||
@PublicAPI(usage = ACCESS) | ||
public SourceCodeLocation getSourceCodeLocation() { | ||
return sourceCodeLocation; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return getClass().getSimpleName() + "{value=" + value + ",lineNumber=" + lineNumber + '}'; | ||
} | ||
|
||
static ReferencedClassObject from(JavaCodeUnit owner, JavaClass javaClass, int lineNumber) { | ||
return new ReferencedClassObject(owner, javaClass, lineNumber); | ||
} | ||
|
||
@PublicAPI(usage = ACCESS) | ||
public static final class Functions { | ||
private Functions() { | ||
} | ||
|
||
@PublicAPI(usage = ACCESS) | ||
public static final ChainableFunction<ReferencedClassObject, JavaClass> GET_VALUE = new ChainableFunction<ReferencedClassObject, JavaClass>() { | ||
@Override | ||
public JavaClass apply(ReferencedClassObject input) { | ||
return input.getValue(); | ||
} | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
archunit/src/main/java/com/tngtech/archunit/core/importer/RawReferencedClassObject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright 2014-2021 TNG Technology Consulting GmbH | ||
* | ||
* 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.tngtech.archunit.core.importer; | ||
|
||
import com.tngtech.archunit.core.domain.JavaClassDescriptor; | ||
|
||
import static com.google.common.base.MoreObjects.toStringHelper; | ||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
class RawReferencedClassObject { | ||
private final JavaClassDescriptor type; | ||
private final int lineNumber; | ||
|
||
private RawReferencedClassObject(JavaClassDescriptor type, int lineNumber) { | ||
this.type = checkNotNull(type); | ||
this.lineNumber = lineNumber; | ||
} | ||
|
||
static RawReferencedClassObject from(JavaClassDescriptor target, int lineNumber) { | ||
return new RawReferencedClassObject(target, lineNumber); | ||
} | ||
|
||
String getClassName() { | ||
return type.getFullyQualifiedClassName(); | ||
} | ||
|
||
int getLineNumber() { | ||
return lineNumber; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return toStringHelper(this) | ||
.add("type", type) | ||
.add("lineNumber", lineNumber) | ||
.toString(); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
archunit/src/test/java/com/tngtech/archunit/core/domain/ReferencedClassObjectTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.tngtech.archunit.core.domain; | ||
|
||
import java.io.File; | ||
|
||
import com.tngtech.archunit.core.importer.ClassFileImporter; | ||
import org.junit.Test; | ||
|
||
import static com.google.common.collect.Iterables.getOnlyElement; | ||
import static com.tngtech.archunit.core.domain.ReferencedClassObject.Functions.GET_VALUE; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class ReferencedClassObjectTest { | ||
|
||
@Test | ||
public void function_getValue() { | ||
class SomeClass { | ||
@SuppressWarnings("unused") | ||
Class<?> call() { | ||
return File.class; | ||
} | ||
} | ||
|
||
JavaClasses classes = new ClassFileImporter().importClasses(SomeClass.class, File.class); | ||
JavaMethod owner = classes.get(SomeClass.class).getMethod("call"); | ||
|
||
ReferencedClassObject referencedClassObject = getOnlyElement(owner.getReferencedClassObjects()); | ||
|
||
assertThat(GET_VALUE.apply(referencedClassObject)).isEqualTo(classes.get(File.class)); | ||
} | ||
} |
Oops, something went wrong.