JavaRecordSupport.java

1
package com.ancientprogramming.fixedformat4j.format.impl;
2
3
import com.ancientprogramming.fixedformat4j.exception.FixedFormatException;
4
5
import java.util.ArrayList;
6
import java.util.List;
7
8
import static java.lang.String.format;
9
10
/**
11
 * Reflective access to the Java {@code record} API (JDK 16+) from bytecode compiled at
12
 * release 11.
13
 *
14
 * <p>A record class can only be loaded by a JVM that has the record API, so whenever
15
 * {@link #isJavaRecord} returns {@code true} the reflective lookups below are guaranteed
16
 * to resolve.
17
 *
18
 * <p>Performance: these lookups run once per record class, inside the
19
 * {@link ClassMetadataCache} build (guarded by {@link ClassValue}); the per-{@code load()}
20
 * hot path only sees the cached {@link ConstructorBinding}.
21
 *
22
 * @author Jacob von Eyben - <a href="https://eybenconsult.com">https://eybenconsult.com</a>
23
 * @since 1.9.0
24
 */
25
final class JavaRecordSupport {
26
27
  private JavaRecordSupport() {
28
  }
29
30
  /**
31
   * Returns whether the given class is a Java {@code record}. Checked via the superclass
32
   * name because {@code java.lang.Record} and {@code Class.isRecord()} are not referenceable
33
   * at release 11; the compiler forbids extending {@code java.lang.Record} explicitly, so the
34
   * check is equivalent for loadable classes.
35
   */
36
  static boolean isJavaRecord(Class<?> clazz) {
37 2 1. isJavaRecord : removed call to java/lang/Class::getSuperclass → SURVIVED
2. isJavaRecord : replaced call to java/lang/Class::getSuperclass with receiver → SURVIVED
    Class<?> superclass = clazz.getSuperclass();
38 11 1. isJavaRecord : negated conditional → SURVIVED
2. isJavaRecord : removed conditional - replaced equality check with true → SURVIVED
3. isJavaRecord : removed conditional - replaced equality check with false → SURVIVED
4. isJavaRecord : removed call to java/lang/Class::getName → SURVIVED
5. isJavaRecord : Substituted 1 with 0 → NO_COVERAGE
6. isJavaRecord : removed conditional - replaced equality check with false → SURVIVED
7. isJavaRecord : removed call to java/lang/String::equals → SURVIVED
8. isJavaRecord : Substituted 0 with 1 → KILLED
9. isJavaRecord : replaced boolean return with true for com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport::isJavaRecord → KILLED
10. isJavaRecord : negated conditional → KILLED
11. isJavaRecord : removed conditional - replaced equality check with true → KILLED
    return superclass != null && "java.lang.Record".equals(superclass.getName());
39
  }
40
41
  /** Name and type of one record component, in declaration order. */
42
  static final class ComponentInfo {
43
    final String name;
44
    final Class<?> type;
45
46
    ComponentInfo(String name, Class<?> type) {
47 1 1. <init> : Removed assignment to member variable name → NO_COVERAGE
      this.name = name;
48 1 1. <init> : Removed assignment to member variable type → NO_COVERAGE
      this.type = type;
49
    }
50
  }
51
52
  /**
53
   * Returns the record components of the given record class in declaration order — the
54
   * exact parameter list of the canonical constructor.
55
   */
56
  static List<ComponentInfo> components(Class<?> recordClass) {
57
    try {
58 5 1. components : replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE
2. components : removed call to java/lang/reflect/Method::invoke → NO_COVERAGE
3. components : removed call to java/lang/Class::getMethod → NO_COVERAGE
4. components : Substituted 0 with 1 → NO_COVERAGE
5. components : Substituted 0 with 1 → NO_COVERAGE
      Object[] components = (Object[]) Class.class.getMethod("getRecordComponents").invoke(recordClass);
59 1 1. components : removed call to java/util/ArrayList::<init> → NO_COVERAGE
      List<ComponentInfo> result = new ArrayList<>(components.length);
60
      for (Object component : components) {
61 6 1. components : removed call to java/lang/Class::getMethod → NO_COVERAGE
2. components : removed call to java/lang/Object::getClass → NO_COVERAGE
3. components : Substituted 0 with 1 → NO_COVERAGE
4. components : Substituted 0 with 1 → NO_COVERAGE
5. components : removed call to java/lang/reflect/Method::invoke → NO_COVERAGE
6. components : replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE
        String name = (String) component.getClass().getMethod("getName").invoke(component);
62 6 1. components : Substituted 0 with 1 → NO_COVERAGE
2. components : Substituted 0 with 1 → NO_COVERAGE
3. components : replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE
4. components : removed call to java/lang/reflect/Method::invoke → NO_COVERAGE
5. components : removed call to java/lang/Class::getMethod → NO_COVERAGE
6. components : removed call to java/lang/Object::getClass → NO_COVERAGE
        Class<?> type = (Class<?>) component.getClass().getMethod("getType").invoke(component);
63 2 1. components : removed call to java/util/List::add → NO_COVERAGE
2. components : removed call to com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport$ComponentInfo::<init> → NO_COVERAGE
        result.add(new ComponentInfo(name, type));
64
      }
65 1 1. components : replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport::components → NO_COVERAGE
      return result;
66
    } catch (ReflectiveOperationException e) {
67 2 1. components : Substituted 1 with 0 → NO_COVERAGE
2. components : Substituted 0 with 1 → NO_COVERAGE
      throw new FixedFormatException(
68 4 1. components : replaced call to java/lang/String::format with argument → NO_COVERAGE
2. components : removed call to java/lang/Class::getName → NO_COVERAGE
3. components : removed call to java/lang/String::format → NO_COVERAGE
4. components : removed call to com/ancientprogramming/fixedformat4j/exception/FixedFormatException::<init> → NO_COVERAGE
          format("unable to read record components of %s", recordClass.getName()), e);
69
    }
70
  }
71
}

Mutations

37

1.1
Location : isJavaRecord
Killed by : none
removed call to java/lang/Class::getSuperclass → SURVIVED
Covering tests

2.2
Location : isJavaRecord
Killed by : none
replaced call to java/lang/Class::getSuperclass with receiver → SURVIVED Covering tests

38

1.1
Location : isJavaRecord
Killed by : none
negated conditional → SURVIVED
Covering tests

2.2
Location : isJavaRecord
Killed by : none
removed conditional - replaced equality check with true → SURVIVED Covering tests

3.3
Location : isJavaRecord
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

4.4
Location : isJavaRecord
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder]/[method:datatype_nonBeanStandardMethod_throwsFixedFormatException()]
Substituted 0 with 1 → KILLED

5.5
Location : isJavaRecord
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder]/[method:datatype_nonBeanStandardMethod_throwsFixedFormatException()]
replaced boolean return with true for com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport::isJavaRecord → KILLED

6.6
Location : isJavaRecord
Killed by : none
removed call to java/lang/Class::getName → SURVIVED Covering tests

7.7
Location : isJavaRecord
Killed by : none
Substituted 1 with 0 → NO_COVERAGE

8.8
Location : isJavaRecord
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder]/[method:datatype_nonBeanStandardMethod_throwsFixedFormatException()]
negated conditional → KILLED

9.9
Location : isJavaRecord
Killed by : none
removed conditional - replaced equality check with false → SURVIVED Covering tests

10.10
Location : isJavaRecord
Killed by : none
removed call to java/lang/String::equals → SURVIVED Covering tests

11.11
Location : isJavaRecord
Killed by : com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder.[engine:junit-jupiter]/[class:com.ancientprogramming.fixedformat4j.format.impl.TestFormatInstructionsBuilder]/[method:datatype_nonBeanStandardMethod_throwsFixedFormatException()]
removed conditional - replaced equality check with true → KILLED

47

1.1
Location : <init>
Killed by : none
Removed assignment to member variable name → NO_COVERAGE

48

1.1
Location : <init>
Killed by : none
Removed assignment to member variable type → NO_COVERAGE

58

1.1
Location : components
Killed by : none
replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE

2.2
Location : components
Killed by : none
removed call to java/lang/reflect/Method::invoke → NO_COVERAGE

3.3
Location : components
Killed by : none
removed call to java/lang/Class::getMethod → NO_COVERAGE

4.4
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

5.5
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

59

1.1
Location : components
Killed by : none
removed call to java/util/ArrayList::<init> → NO_COVERAGE

61

1.1
Location : components
Killed by : none
removed call to java/lang/Class::getMethod → NO_COVERAGE

2.2
Location : components
Killed by : none
removed call to java/lang/Object::getClass → NO_COVERAGE

3.3
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

4.4
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

5.5
Location : components
Killed by : none
removed call to java/lang/reflect/Method::invoke → NO_COVERAGE

6.6
Location : components
Killed by : none
replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE

62

1.1
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

2.2
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

3.3
Location : components
Killed by : none
replaced call to java/lang/reflect/Method::invoke with argument → NO_COVERAGE

4.4
Location : components
Killed by : none
removed call to java/lang/reflect/Method::invoke → NO_COVERAGE

5.5
Location : components
Killed by : none
removed call to java/lang/Class::getMethod → NO_COVERAGE

6.6
Location : components
Killed by : none
removed call to java/lang/Object::getClass → NO_COVERAGE

63

1.1
Location : components
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

2.2
Location : components
Killed by : none
removed call to com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport$ComponentInfo::<init> → NO_COVERAGE

65

1.1
Location : components
Killed by : none
replaced return value with Collections.emptyList for com/ancientprogramming/fixedformat4j/format/impl/JavaRecordSupport::components → NO_COVERAGE

67

1.1
Location : components
Killed by : none
Substituted 1 with 0 → NO_COVERAGE

2.2
Location : components
Killed by : none
Substituted 0 with 1 → NO_COVERAGE

68

1.1
Location : components
Killed by : none
replaced call to java/lang/String::format with argument → NO_COVERAGE

2.2
Location : components
Killed by : none
removed call to java/lang/Class::getName → NO_COVERAGE

3.3
Location : components
Killed by : none
removed call to java/lang/String::format → NO_COVERAGE

4.4
Location : components
Killed by : none
removed call to com/ancientprogramming/fixedformat4j/exception/FixedFormatException::<init> → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.23.1 support