Skip to content

Commit

Permalink
Add support to test caught exceptions (fixes TNG#591)
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Sierszeń <krzysztof.sierszen@digitalnewagency.com>
  • Loading branch information
crizzis committed Feb 25, 2022
1 parent 8f56568 commit 4d25f06
Show file tree
Hide file tree
Showing 30 changed files with 1,167 additions and 535 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,38 @@
*/
package com.tngtech.archunit.core.domain;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.AccessTarget.CodeUnitCallTarget;
import com.tngtech.archunit.core.domain.JavaAccess.Predicates.TargetPredicate;
import com.tngtech.archunit.core.importer.CaughtThrowable;
import com.tngtech.archunit.core.importer.DomainBuilders;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaAccessBuilder;

import java.util.Set;

import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;

public abstract class JavaCall<T extends CodeUnitCallTarget> extends JavaCodeUnitAccess<T> {
JavaCall(JavaAccessBuilder<T, ?> builder) {
private static final Function<JavaClassDescriptor, CaughtThrowable> TO_CAUGHT_THROWABLE_CONVERTER = new Function<JavaClassDescriptor, CaughtThrowable>() {

@Override
public CaughtThrowable apply(JavaClassDescriptor input) {
return new CaughtThrowable(input);
}
};
private Set<CaughtThrowable> caughtThrowables;

JavaCall(DomainBuilders.JavaCallBuilder<T, ? extends JavaCall<T>, ?> builder) {
super(builder);
caughtThrowables = Sets.newHashSet(Iterables.transform(builder.getCaughtThrowables(), TO_CAUGHT_THROWABLE_CONVERTER));
}

public Set<CaughtThrowable> getCaughtThrowables() {
return caughtThrowables;
}

public static final class Predicates {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.tngtech.archunit.core.importer.DomainBuilders.JavaConstructorCallBuilder;

public class JavaConstructorCall extends JavaCall<ConstructorCallTarget> {

JavaConstructorCall(JavaConstructorCallBuilder builder) {
super(builder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodCallBuilder;

public class JavaMethodCall extends JavaCall<MethodCallTarget> {

JavaMethodCall(JavaMethodCallBuilder builder) {
super(builder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
*/
package com.tngtech.archunit.core.importer;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
Expand All @@ -27,12 +24,7 @@
import com.tngtech.archunit.Internal;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.AccessTarget;
import com.tngtech.archunit.core.domain.AccessTarget.CodeUnitAccessTarget;
import com.tngtech.archunit.core.domain.AccessTarget.ConstructorCallTarget;
import com.tngtech.archunit.core.domain.AccessTarget.ConstructorReferenceTarget;
import com.tngtech.archunit.core.domain.AccessTarget.FieldAccessTarget;
import com.tngtech.archunit.core.domain.AccessTarget.MethodCallTarget;
import com.tngtech.archunit.core.domain.AccessTarget.MethodReferenceTarget;
import com.tngtech.archunit.core.domain.AccessTarget.*;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClassDescriptor;
import com.tngtech.archunit.core.domain.JavaCodeUnit;
Expand Down Expand Up @@ -63,16 +55,31 @@ interface FieldAccessRecord extends AccessRecord<FieldAccessTarget> {
AccessType getAccessType();
}

@Internal
interface CallRecord<T extends CodeUnitCallTarget> extends AccessRecord<T> {
Set<JavaClassDescriptor> getCaughtThrowables();
}

@Internal
interface MethodCallRecord extends CallRecord<MethodCallTarget> {

}

@Internal
interface ConstructorCallRecord extends CallRecord<ConstructorCallTarget> {

}

@Internal
abstract class Factory<RAW_RECORD, PROCESSED_RECORD> {

abstract PROCESSED_RECORD create(RAW_RECORD record, ImportedClasses classes);

static Factory<RawAccessRecord, AccessRecord<ConstructorCallTarget>> forConstructorCallRecord() {
return new Factory<RawAccessRecord, AccessRecord<ConstructorCallTarget>>() {
static Factory<RawAccessRecord.ForMethodCall, ConstructorCallRecord> forConstructorCallRecord() {
return new Factory<RawAccessRecord.ForMethodCall, ConstructorCallRecord>() {
@Override
AccessRecord<ConstructorCallTarget> create(RawAccessRecord record, ImportedClasses classes) {
return new RawAccessRecordProcessed<>(record, classes, CONSTRUCTOR_CALL_TARGET_FACTORY);
ConstructorCallRecord create(RawAccessRecord.ForMethodCall record, ImportedClasses classes) {
return new RawConstructorCallRecordProcessed(record, classes);
}
};
}
Expand All @@ -86,11 +93,11 @@ AccessRecord<ConstructorReferenceTarget> create(RawAccessRecord record, Imported
};
}

static Factory<RawAccessRecord, AccessRecord<MethodCallTarget>> forMethodCallRecord() {
return new Factory<RawAccessRecord, AccessRecord<MethodCallTarget>>() {
static Factory<RawAccessRecord.ForMethodCall, MethodCallRecord> forMethodCallRecord() {
return new Factory<RawAccessRecord.ForMethodCall, MethodCallRecord>() {
@Override
AccessRecord<MethodCallTarget> create(RawAccessRecord record, ImportedClasses classes) {
return new RawAccessRecordProcessed<>(record, classes, METHOD_CALL_TARGET_FACTORY);
MethodCallRecord create(RawAccessRecord.ForMethodCall record, ImportedClasses classes) {
return new RawMethodCallRecordProcessed(record, classes);
}
};
}
Expand Down Expand Up @@ -307,6 +314,33 @@ public AccessType getAccessType() {
}
}

private abstract static class RawCodeUnitCallRecordProcessed<T extends CodeUnitCallTarget> extends RawAccessRecordProcessed<T> {
private final Set<JavaClassDescriptor> caughtThrowables;

RawCodeUnitCallRecordProcessed(RawAccessRecord.ForMethodCall record, ImportedClasses classes, AccessTargetFactory<T> accessTargetFactory) {
super(record, classes, accessTargetFactory);
caughtThrowables = record.caughtThrowables;
}

public Set<JavaClassDescriptor> getCaughtThrowables() {
return caughtThrowables;
}
}

private static class RawConstructorCallRecordProcessed extends RawCodeUnitCallRecordProcessed<ConstructorCallTarget> implements ConstructorCallRecord {

RawConstructorCallRecordProcessed(RawAccessRecord.ForMethodCall record, ImportedClasses classes) {
super(record, classes, CONSTRUCTOR_CALL_TARGET_FACTORY);
}
}

private static class RawMethodCallRecordProcessed extends RawCodeUnitCallRecordProcessed<MethodCallTarget> implements MethodCallRecord {

RawMethodCallRecordProcessed(RawAccessRecord.ForMethodCall record, ImportedClasses classes) {
super(record, classes, METHOD_CALL_TARGET_FACTORY);
}
}

private static Supplier<JavaCodeUnit> createOriginSupplier(final CodeUnit origin, final ImportedClasses classes) {
return Suppliers.memoize(new Supplier<JavaCodeUnit>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2014-2022 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;

public class CaughtThrowable {

private final JavaClassDescriptor throwableType;

public CaughtThrowable(JavaClassDescriptor throwableType) {
this.throwableType = throwableType;
}

public JavaClassDescriptor getThrowableType() {
return throwableType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class ClassFileImportRecord {
private final EnclosingDeclarationsByInnerClasses enclosingDeclarationsByOwner = new EnclosingDeclarationsByInnerClasses();

private final Set<RawAccessRecord.ForField> rawFieldAccessRecords = new HashSet<>();
private final Set<RawAccessRecord> rawMethodCallRecords = new HashSet<>();
private final Set<RawAccessRecord> rawConstructorCallRecords = new HashSet<>();
private final Set<RawAccessRecord.ForMethodCall> rawMethodCallRecords = new HashSet<>();
private final Set<RawAccessRecord.ForMethodCall> rawConstructorCallRecords = new HashSet<>();
private final Set<RawAccessRecord> rawMethodReferenceRecords = new HashSet<>();
private final Set<RawAccessRecord> rawConstructorReferenceRecords = new HashSet<>();

Expand Down Expand Up @@ -242,11 +242,11 @@ void registerFieldAccess(RawAccessRecord.ForField record) {
rawFieldAccessRecords.add(record);
}

void registerMethodCall(RawAccessRecord record) {
void registerMethodCall(RawAccessRecord.ForMethodCall record) {
rawMethodCallRecords.add(record);
}

void registerConstructorCall(RawAccessRecord record) {
void registerConstructorCall(RawAccessRecord.ForMethodCall record) {
rawConstructorCallRecords.add(record);
}

Expand All @@ -262,11 +262,11 @@ Set<RawAccessRecord.ForField> getRawFieldAccessRecords() {
return ImmutableSet.copyOf(rawFieldAccessRecords);
}

Set<RawAccessRecord> getRawMethodCallRecords() {
Set<RawAccessRecord.ForMethodCall> getRawMethodCallRecords() {
return ImmutableSet.copyOf(rawMethodCallRecords);
}

Set<RawAccessRecord> getRawConstructorCallRecords() {
Set<RawAccessRecord.ForMethodCall> getRawConstructorCallRecords() {
return ImmutableSet.copyOf(rawConstructorCallRecords);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,20 @@
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaAnnotationBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaClassTypeParametersBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaConstructorBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaFieldBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaMethodBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaParameterizedTypeBuilder;
import com.tngtech.archunit.core.importer.DomainBuilders.JavaStaticInitializerBuilder;
import com.tngtech.archunit.core.importer.JavaClassProcessor.AccessHandler;
import com.tngtech.archunit.core.importer.RawAccessRecord.CodeUnit;
import com.tngtech.archunit.core.importer.RawAccessRecord.TargetInfo;
import com.tngtech.archunit.core.importer.resolvers.ClassResolver;
import com.tngtech.archunit.core.importer.resolvers.ClassResolver.ClassUriImporter;
import org.objectweb.asm.ClassReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME;
import static org.objectweb.asm.Opcodes.ASM9;

class ClassFileProcessor {
Expand All @@ -56,7 +52,8 @@ class ClassFileProcessor {
JavaClasses process(ClassFileSource source) {
ClassFileImportRecord importRecord = new ClassFileImportRecord();
DependencyResolutionProcess dependencyResolutionProcess = new DependencyResolutionProcess();
RecordAccessHandler accessHandler = new RecordAccessHandler(importRecord, dependencyResolutionProcess);
TryCatchTracker tryCatchTracker = new TryCatchTracker();
RecordAccessHandler accessHandler = new RecordAccessHandler(importRecord, dependencyResolutionProcess, tryCatchTracker);
ClassDetailsRecorder classDetailsRecorder = new ClassDetailsRecorder(importRecord, dependencyResolutionProcess);
for (ClassFileLocation location : source) {
try (InputStream s = location.openStream()) {
Expand Down Expand Up @@ -196,72 +193,6 @@ public void onDeclaredGenericSignatureType(String typeName) {
}
}

private static class RecordAccessHandler implements AccessHandler {
private static final Logger LOG = LoggerFactory.getLogger(RecordAccessHandler.class);

private final ClassFileImportRecord importRecord;
private final DependencyResolutionProcess dependencyResolutionProcess;
private CodeUnit codeUnit;
private int lineNumber;

private RecordAccessHandler(ClassFileImportRecord importRecord, DependencyResolutionProcess dependencyResolutionProcess) {
this.importRecord = importRecord;
this.dependencyResolutionProcess = dependencyResolutionProcess;
}

@Override
public void setContext(CodeUnit codeUnit) {
this.codeUnit = codeUnit;
}

@Override
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}

@Override
public void handleFieldInstruction(int opcode, String owner, String name, String desc) {
AccessType accessType = AccessType.forOpCode(opcode);
LOG.trace("Found {} access to field {}.{}:{} in line {}", accessType, owner, name, desc, lineNumber);
TargetInfo target = new TargetInfo(owner, name, desc);
importRecord.registerFieldAccess(filled(new RawAccessRecord.ForField.Builder(), target)
.withAccessType(accessType)
.build());
dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName());
}

@Override
public void handleMethodInstruction(String owner, String name, String desc) {
LOG.trace("Found call of method {}.{}:{} in line {}", owner, name, desc, lineNumber);
TargetInfo target = new TargetInfo(owner, name, desc);
if (CONSTRUCTOR_NAME.equals(name)) {
importRecord.registerConstructorCall(filled(new RawAccessRecord.Builder(), target).build());
} else {
importRecord.registerMethodCall(filled(new RawAccessRecord.Builder(), target).build());
}
dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName());
}

@Override
public void handleMethodReferenceInstruction(String owner, String name, String desc) {
LOG.trace("Found method reference {}.{}:{} in line {}", owner, name, desc, lineNumber);
TargetInfo target = new TargetInfo(owner, name, desc);
if (CONSTRUCTOR_NAME.equals(name)) {
importRecord.registerConstructorReference(filled(new RawAccessRecord.Builder(), target).build());
} else {
importRecord.registerMethodReference(filled(new RawAccessRecord.Builder(), target).build());
}
dependencyResolutionProcess.registerAccessToType(target.owner.getFullyQualifiedClassName());
}

private <BUILDER extends RawAccessRecord.BaseBuilder<BUILDER>> BUILDER filled(BUILDER builder, TargetInfo target) {
return builder
.withCaller(codeUnit)
.withTarget(target)
.withLineNumber(lineNumber);
}
}

private ClassResolver getClassResolver(ClassDetailsRecorder classDetailsRecorder) {
ClassResolver classResolver = classResolverFactory.create();
classResolver.setClassUriImporter(new UriImporterOfProcessor(classDetailsRecorder, md5InClassSourcesEnabled));
Expand Down
Loading

0 comments on commit 4d25f06

Please sign in to comment.