• 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 import java.lang.IllegalArgumentException
18 
19 private val BASE_INCLUDED_CLASS_NAME_GLOBS = listOf(
20     "**", // everything
21 )
22 
23 private val BASE_EXCLUDED_CLASS_NAME_GLOBS = listOf(
24     "\\[**", // array types
25     "com.code_intelligence.jazzer.**",
26     "com.sun.**", // package for Proxy objects
27     "java.**",
28     "javax.**",
29     "jaz.Ter", // safe companion of the honeypot class used by sanitizers
30     "jaz.Zer", // honeypot class used by sanitizers
31     "jdk.**",
32     "kotlin.**",
33     "sun.**",
34 )
35 
36 class ClassNameGlobber(includes: List<String>, excludes: List<String>) {
37     // If no include globs are provided, start with all classes.
38     private val includeMatchers = (if (includes.isEmpty()) BASE_INCLUDED_CLASS_NAME_GLOBS else includes)
39         .map(::SimpleGlobMatcher)
40 
41     // If no include globs are provided, additionally exclude stdlib classes as well as our own classes.
42     private val excludeMatchers = (if (includes.isEmpty()) BASE_EXCLUDED_CLASS_NAME_GLOBS + excludes else excludes)
43         .map(::SimpleGlobMatcher)
44 
includesnull45     fun includes(className: String): Boolean {
46         return includeMatchers.any { it.matches(className) } && excludeMatchers.none { it.matches(className) }
47     }
48 }
49 
50 class SimpleGlobMatcher(val glob: String) {
51     private enum class Type {
52         // foo.bar (matches foo.bar only)
53         FULL_MATCH,
54         // foo.** (matches foo.bar and foo.bar.baz)
55         PATH_WILDCARD_SUFFIX,
56         // foo.* (matches foo.bar, but not foo.bar.baz)
57         SEGMENT_WILDCARD_SUFFIX,
58     }
59 
60     private val type: Type
61     private val prefix: String
62 
63     init {
64         // Remain compatible with globs such as "\\[" that use escaping.
65         val pattern = glob.replace("\\", "")
66         when {
67             !pattern.contains('*') -> {
68                 type = Type.FULL_MATCH
69                 prefix = pattern
70             }
71             // Ends with "**" and contains no other '*'.
72             pattern.endsWith("**") && pattern.indexOf('*') == pattern.length - 2 -> {
73                 type = Type.PATH_WILDCARD_SUFFIX
74                 prefix = pattern.removeSuffix("**")
75             }
76             // Ends with "*" and contains no other '*'.
77             pattern.endsWith('*') && pattern.indexOf('*') == pattern.length - 1 -> {
78                 type = Type.SEGMENT_WILDCARD_SUFFIX
79                 prefix = pattern.removeSuffix("*")
80             }
81             else -> throw IllegalArgumentException(
82                 "Unsupported glob pattern (only foo.bar, foo.* and foo.** are supported): $pattern"
83             )
84         }
85     }
86 
87     /**
88      * Checks whether [maybeInternalClassName], which may be internal (foo/bar) or not (foo.bar), matches [glob].
89      */
matchesnull90     fun matches(maybeInternalClassName: String): Boolean {
91         val className = maybeInternalClassName.replace('/', '.')
92         return when (type) {
93             Type.FULL_MATCH -> className == prefix
94             Type.PATH_WILDCARD_SUFFIX -> className.startsWith(prefix)
95             Type.SEGMENT_WILDCARD_SUFFIX -> {
96                 // className starts with prefix and contains no further '.'.
97                 className.startsWith(prefix) &&
98                     className.indexOf('.', startIndex = prefix.length) == -1
99             }
100         }
101     }
102 }
103