• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.code_intelligence.jazzer.utils
16 
17 private val BASE_INCLUDED_CLASS_NAME_GLOBS = listOf(
18     "**", // everything
19 )
20 
21 // We use both a strong indicator for running as a Bazel test together with an indicator for a
22 // Bazel coverage run to rule out false positives.
23 private val IS_BAZEL_COVERAGE_RUN = System.getenv("TEST_UNDECLARED_OUTPUTS_DIR") != null &&
24     System.getenv("COVERAGE_DIR") != null
25 
26 private val ADDITIONAL_EXCLUDED_NAME_GLOBS_FOR_BAZEL_COVERAGE = listOf(
27     "com.google.testing.coverage.**",
28     "org.jacoco.**",
29 )
30 
31 private val BASE_EXCLUDED_CLASS_NAME_GLOBS = listOf(
32     // JDK internals
33     "\\[**", // array types
34     "java.**",
35     "javax.**",
36     "jdk.**",
37     "sun.**",
38     "com.sun.**", // package for Proxy objects
39     // Azul JDK internals
40     "com.azul.tooling.**",
41     // Kotlin internals
42     "kotlin.**",
43     // Jazzer internals
44     "com.code_intelligence.jazzer.**",
45     "jaz.Ter", // safe companion of the honeypot class used by sanitizers
46     "jaz.Zer", // honeypot class used by sanitizers
47 ) + if (IS_BAZEL_COVERAGE_RUN) ADDITIONAL_EXCLUDED_NAME_GLOBS_FOR_BAZEL_COVERAGE else listOf()
48 
49 class ClassNameGlobber(includes: List<String>, excludes: List<String>) {
50     // If no include globs are provided, start with all classes.
<lambda>null51     private val includeMatchers = includes.ifEmpty { BASE_INCLUDED_CLASS_NAME_GLOBS }
52         .map(::SimpleGlobMatcher)
53 
54     // If no include globs are provided, additionally exclude stdlib classes as well as our own classes.
55     private val excludeMatchers = (if (includes.isEmpty()) BASE_EXCLUDED_CLASS_NAME_GLOBS + excludes else excludes)
56         .map(::SimpleGlobMatcher)
57 
includesnull58     fun includes(className: String): Boolean {
59         return includeMatchers.any { it.matches(className) } && excludeMatchers.none { it.matches(className) }
60     }
61 }
62 
63 class SimpleGlobMatcher(val glob: String) {
64     private enum class Type {
65         // foo.bar (matches foo.bar only)
66         FULL_MATCH,
67         // foo.** (matches foo.bar and foo.bar.baz)
68         PATH_WILDCARD_SUFFIX,
69         // foo.* (matches foo.bar, but not foo.bar.baz)
70         SEGMENT_WILDCARD_SUFFIX,
71     }
72 
73     private val type: Type
74     private val prefix: String
75 
76     init {
77         // Remain compatible with globs such as "\\[" that use escaping.
78         val pattern = glob.replace("\\", "")
79         when {
80             !pattern.contains('*') -> {
81                 type = Type.FULL_MATCH
82                 prefix = pattern
83             }
84             // Ends with "**" and contains no other '*'.
85             pattern.endsWith("**") && pattern.indexOf('*') == pattern.length - 2 -> {
86                 type = Type.PATH_WILDCARD_SUFFIX
87                 prefix = pattern.removeSuffix("**")
88             }
89             // Ends with "*" and contains no other '*'.
90             pattern.endsWith('*') && pattern.indexOf('*') == pattern.length - 1 -> {
91                 type = Type.SEGMENT_WILDCARD_SUFFIX
92                 prefix = pattern.removeSuffix("*")
93             }
94             else -> throw IllegalArgumentException(
95                 "Unsupported glob pattern (only foo.bar, foo.* and foo.** are supported): $pattern"
96             )
97         }
98     }
99 
100     /**
101      * Checks whether [maybeInternalClassName], which may be internal (foo/bar) or not (foo.bar), matches [glob].
102      */
matchesnull103     fun matches(maybeInternalClassName: String): Boolean {
104         val className = maybeInternalClassName.replace('/', '.')
105         return when (type) {
106             Type.FULL_MATCH -> className == prefix
107             Type.PATH_WILDCARD_SUFFIX -> className.startsWith(prefix)
108             Type.SEGMENT_WILDCARD_SUFFIX -> {
109                 // className starts with prefix and contains no further '.'.
110                 className.startsWith(prefix) &&
111                     className.indexOf('.', startIndex = prefix.length) == -1
112             }
113         }
114     }
115 }
116