1
2
3
4
5 package mockit.coverage.modification;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import java.security.ProtectionDomain;
11 import java.util.ArrayList;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Set;
15
16 import mockit.asm.classes.ClassReader;
17
18 public final class ClassModification {
19 @NonNull
20 private final Set<String> modifiedClasses;
21 @NonNull
22 final List<ProtectionDomain> protectionDomainsWithUniqueLocations;
23 @NonNull
24 private final ClassSelection classSelection;
25
26 public ClassModification() {
27 modifiedClasses = new HashSet<>();
28 protectionDomainsWithUniqueLocations = new ArrayList<>();
29 classSelection = new ClassSelection();
30 }
31
32 public boolean shouldConsiderClassesNotLoaded() {
33 return !classSelection.loadedOnly;
34 }
35
36 boolean isToBeConsideredForCoverage(@NonNull String className, @NonNull ProtectionDomain protectionDomain) {
37 return !modifiedClasses.contains(className) && classSelection.isSelected(className, protectionDomain);
38 }
39
40 @Nullable
41 public byte[] modifyClass(@NonNull String className, @NonNull ProtectionDomain protectionDomain,
42 @NonNull byte[] originalClassfile) {
43 if (isToBeConsideredForCoverage(className, protectionDomain)) {
44 try {
45 byte[] modifiedClassfile = modifyClassForCoverage(className, originalClassfile);
46 registerModifiedClass(className, protectionDomain);
47 return modifiedClassfile;
48 } catch (VisitInterruptedException ignore) {
49
50 } catch (RuntimeException | AssertionError | ClassCircularityError e) {
51 e.printStackTrace();
52 }
53 }
54
55 return null;
56 }
57
58 @NonNull
59 private static byte[] modifyClassForCoverage(@NonNull String className, @NonNull byte[] classBytecode) {
60 byte[] modifiedBytecode = CoverageModifier.recoverModifiedByteCodeIfAvailable(className);
61
62 if (modifiedBytecode != null) {
63 return modifiedBytecode;
64 }
65
66 ClassReader cr = new ClassReader(classBytecode);
67 CoverageModifier modifier = new CoverageModifier(cr);
68 cr.accept(modifier);
69 return modifier.toByteArray();
70 }
71
72 private void registerModifiedClass(@NonNull String className, @NonNull ProtectionDomain pd) {
73 modifiedClasses.add(className);
74
75 if (pd.getClassLoader() != null && pd.getCodeSource() != null && pd.getCodeSource().getLocation() != null) {
76 addProtectionDomainIfHasUniqueNewPath(pd);
77 }
78 }
79
80 private void addProtectionDomainIfHasUniqueNewPath(@NonNull ProtectionDomain newPD) {
81 String newPath = newPD.getCodeSource().getLocation().getPath();
82
83 for (int i = protectionDomainsWithUniqueLocations.size() - 1; i >= 0; i--) {
84 ProtectionDomain previousPD = protectionDomainsWithUniqueLocations.get(i);
85 String previousPath = previousPD.getCodeSource().getLocation().getPath();
86
87 if (previousPath.startsWith(newPath)) {
88 return;
89 }
90 if (newPath.startsWith(previousPath)) {
91 protectionDomainsWithUniqueLocations.set(i, newPD);
92 return;
93 }
94 }
95
96 protectionDomainsWithUniqueLocations.add(newPD);
97 }
98 }