Skip to content

Commit

Permalink
recognize empty records
Browse files Browse the repository at this point in the history
As `ClassVisitor#visitRecordComponent` is not invoked for empty records (without components),
we better rely on the access flags provided to `ClassVisitor#visit`.

Signed-off-by: Manfred Hanke <[email protected]>
  • Loading branch information
hankem authored and codecholeric committed Nov 21, 2022
1 parent 165dbbd commit 2cad383
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@
import com.tngtech.archunit.core.domain.JavaConstructor;
import com.tngtech.archunit.core.domain.JavaField;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.junit.Test;
import org.junit.runner.RunWith;

import static com.tngtech.archunit.testutil.Assertions.assertThat;
import static com.tngtech.java.junit.dataprovider.DataProviders.testForEach;

@RunWith(DataProviderRunner.class)
public class ClassFileImporterRecordsTest {

@Test
public void imports_simple_record() {
record RecordToImport(String component1, int component2) {
@DataProvider
public static Object[][] imports_record() {
record SimpleRecord(String component1, int component2) {
}
record EmptyRecord() {
}
return testForEach(SimpleRecord.class, EmptyRecord.class);
}

JavaClass javaClass = new ClassFileImporter().importClasses(RecordToImport.class, Record.class).get(RecordToImport.class);
@Test
@UseDataProvider
public void imports_record(Class<?> recordToImport) {
JavaClass javaClass = new ClassFileImporter().importClasses(recordToImport, Record.class).get(recordToImport);

assertThat(javaClass)
.matches(RecordToImport.class)
.matches(recordToImport)
.hasRawSuperclassMatching(Record.class)
.hasNoInterfaces()
.isInterface(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.EnumSet;
import java.util.Set;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.tngtech.archunit.PublicAPI;
import org.objectweb.asm.Opcodes;
Expand Down Expand Up @@ -73,7 +74,17 @@ public enum JavaModifier {
@Deprecated
@PublicAPI(usage = ACCESS)
public static Set<JavaModifier> getModifiersForClass(int asmAccess) {
return getModifiersFor(ApplicableType.CLASS, asmAccess);
Set<JavaModifier> modifiers = getModifiersFor(ApplicableType.CLASS, asmAccess);
boolean opCodeForRecordIsPresent = (asmAccess & Opcodes.ACC_RECORD) != 0;
if (opCodeForRecordIsPresent) {
// As records are implicitly static and final (compare JLS 8.10 Record Declarations),
// we ensure that those modifiers are always present. (asmAccess does not contain STATIC.)
return ImmutableSet.<JavaModifier>builder()
.addAll(modifiers)
.add(JavaModifier.STATIC, JavaModifier.FINAL)
.build();
}
return modifiers;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Bytes;
Expand Down Expand Up @@ -58,7 +57,6 @@
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.RecordComponentVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -114,6 +112,7 @@ public void visit(int version, int access, String name, String signature, String
boolean opCodeForInterfaceIsPresent = (access & Opcodes.ACC_INTERFACE) != 0;
boolean opCodeForEnumIsPresent = (access & Opcodes.ACC_ENUM) != 0;
boolean opCodeForAnnotationIsPresent = (access & Opcodes.ACC_ANNOTATION) != 0;
boolean opCodeForRecordIsPresent = (access & Opcodes.ACC_RECORD) != 0;
Optional<String> superclassName = getSuperclassName(superName, opCodeForInterfaceIsPresent);
LOG.trace("Found superclass {} on class '{}'", superclassName.orElse(null), name);

Expand All @@ -123,7 +122,8 @@ public void visit(int version, int access, String name, String signature, String
.withInterface(opCodeForInterfaceIsPresent)
.withEnum(opCodeForEnumIsPresent)
.withAnnotation(opCodeForAnnotationIsPresent)
.withModifiers(JavaModifier.getModifiersForClass(access));
.withModifiers(JavaModifier.getModifiersForClass(access))
.withRecord(opCodeForRecordIsPresent);

className = descriptor.getFullyQualifiedClassName();
declarationHandler.onNewClass(className, superclassName, interfaceNames);
Expand Down Expand Up @@ -153,21 +153,6 @@ public void visitSource(String source, String debug) {
}
}

@Override
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
javaClassBuilder.withRecord(true);

// Records are implicitly static and final (compare JLS 8.10 Record Declarations)
// Thus we ensure that those modifiers are always present (the access flag in visit(..) does not contain STATIC)
ImmutableSet<JavaModifier> recordModifiers = ImmutableSet.<JavaModifier>builder()
.addAll(javaClassBuilder.getModifiers())
.add(JavaModifier.STATIC, JavaModifier.FINAL)
.build();
javaClassBuilder.withModifiers(recordModifiers);

return super.visitRecordComponent(name, descriptor, signature);
}

@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (importAborted()) {
Expand Down

0 comments on commit 2cad383

Please sign in to comment.