Skip to content

Commit 66ec435

Browse files
authored
Feature unknown classes (#148)
Support unknown classes/method/fields to be resolved to some VirtualClass #147
1 parent c5aa437 commit 66ec435

File tree

30 files changed

+738
-236
lines changed

30 files changed

+738
-236
lines changed

build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ val includeDokka: String? by project
99

1010
group = "org.jacodb"
1111

12-
project.version = semVer ?: "1.1-SNAPSHOT"
12+
project.version = semVer ?: "1.2-SNAPSHOT"
1313

1414
buildscript {
1515
repositories {

jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/BaseAnalysisTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.jacodb.api.ext.findClass
2626
import org.jacodb.api.ext.methods
2727
import org.jacodb.impl.features.InMemoryHierarchy
2828
import org.jacodb.impl.features.Usages
29+
import org.jacodb.impl.features.classpaths.UnknownClasses
2930
import org.jacodb.impl.features.hierarchyExt
3031
import org.jacodb.testing.BaseTest
3132
import org.jacodb.testing.WithDB
@@ -37,7 +38,7 @@ import java.util.stream.Stream
3738
import kotlin.streams.asStream
3839

3940
abstract class BaseAnalysisTest : BaseTest() {
40-
companion object : WithDB(Usages, InMemoryHierarchy) {
41+
companion object : WithDB(UnknownClasses, Usages, InMemoryHierarchy) {
4142
@JvmStatic
4243
fun provideClassesForJuliet(cweNum: Int, cweSpecificBans: List<String> = emptyList()): Stream<Arguments> = runBlocking {
4344
val cp = db.classpath(allClasspath)

jacodb-api/README.md

+1-8
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,4 @@ Call on each step of a lifecycle with respected signal.
4343
[BeforeIndexing]: https://jacodb.org/docs/jacodb-api/org.jacodb.api/-jc-signal/-before-indexing/index.html
4444
[AfterIndexing]: https://jacodb.org/docs/jacodb-api/org.jacodb.api/-jc-signal/-after-indexing/index.html
4545
[LocationRemoved]: https://jacodb.org/docs/jacodb-api/org.jacodb.api/-jc-signal/-location-removed/index.html
46-
[Drop]: https://jacodb.org/docs/jacodb-api/jacodb-api/org.jacodb.api/-jc-signal/-drop/index.html
47-
48-
49-
50-
51-
52-
53-
46+
[Drop]: https://jacodb.org/docs/jacodb-api/jacodb-api/org.jacodb.api/-jc-signal/-drop/index.html

jacodb-api/src/main/kotlin/org/jacodb/api/Classes.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ interface JcClassOrInterface : JcAnnotatedSymbol, JcAccessible {
4848

4949
fun <T> extensionValue(key: String): T?
5050

51+
val lookup: JcLookup<JcField, JcMethod>
5152

5253
val isAnnotation: Boolean
5354
get() {
@@ -125,7 +126,6 @@ interface JcMethod : JcSymbol, JcAnnotatedSymbol, JcAccessible {
125126
return name == "<clinit>"
126127
}
127128

128-
129129
}
130130

131131
interface JcField : JcAnnotatedSymbol, JcAccessible {
@@ -144,4 +144,5 @@ interface JcParameter : JcAnnotated, JcAccessible {
144144

145145
interface TypeName {
146146
val typeName: String
147-
}
147+
}
148+

jacodb-api/src/main/kotlin/org/jacodb/api/JcClasspath.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ interface JcClasspath : Closeable {
8383
suspend fun <T : JcClasspathTask> execute(task: T): T
8484

8585
fun <T : JcClasspathTask> executeAsync(task: T): Future<T> = GlobalScope.future { execute(task) }
86+
87+
fun isInstalled(feature: JcClasspathFeature): Boolean
8688
}
8789

8890

@@ -205,4 +207,4 @@ interface JcMethodExtFeature : JcClasspathFeature {
205207
fun instList(method: JcMethod): JcInstListResult? = null
206208
fun rawInstList(method: JcMethod): JcRawInstListResult? = null
207209

208-
}
210+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2022 UnitTestBot contributors (utbot.org)
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jacodb.api
18+
19+
/**
20+
* lookup for fields and methods in [JcClassOrInterface] and [JcClassType]
21+
*/
22+
@JvmDefaultWithoutCompatibility
23+
interface JcLookup<Field : JcAccessible, Method : JcAccessible> {
24+
25+
fun field(name: String): Field? = field(name, null)
26+
fun field(name: String, typeName: TypeName?): Field?
27+
fun method(name: String, description: String): Method?
28+
29+
fun staticMethod(name: String, description: String): Method?
30+
fun specialMethod(name: String, description: String): Method?
31+
}

jacodb-api/src/main/kotlin/org/jacodb/api/Types.kt

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ interface JcClassType : JcRefType, JcAccessible {
100100
val interfaces: List<JcClassType>
101101

102102
val innerTypes: List<JcClassType>
103+
104+
val lookup: JcLookup<JcTypedField, JcTypedMethod>
105+
103106
}
104107

105108
interface JcTypeVariable : JcRefType {

jacodb-api/src/main/kotlin/org/jacodb/api/ext/JcClasses.kt

+3-30
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,7 @@ const val JAVA_OBJECT = "java.lang.Object"
5858
* find field by name
5959
*/
6060
fun JcClassOrInterface.findFieldOrNull(name: String): JcField? {
61-
return findElements(
62-
allowSearchAll = true,
63-
packageName = { packageName },
64-
getAccessibles = { declaredFields },
65-
nextHierarchy = { superClass?.let { listOf(it) } },
66-
) {
67-
it.name == name
68-
}
61+
return lookup.field(name)
6962
}
7063

7164
fun JcClassOrInterface.findDeclaredFieldOrNull(name: String): JcField? = declaredFields.singleOrNull { it.name == name }
@@ -81,28 +74,8 @@ fun JcClassOrInterface.findDeclaredMethodOrNull(name: String, desc: String? = nu
8174
/**
8275
* find method by name and description
8376
*/
84-
fun JcClassOrInterface.findMethodOrNull(name: String, desc: String? = null): JcMethod? {
85-
// let's find method based on strict hierarchy
86-
// if method is not found then it's defined in interfaces
87-
val predicate: (JcMethod) -> Boolean = {
88-
it.name == name && (desc == null || it.description == desc)
89-
}
90-
val method = findElements(
91-
packageName = { packageName },
92-
getAccessibles = { declaredMethods },
93-
nextHierarchy = { superClass?.let { listOf(it) } },
94-
predicate = predicate
95-
)
96-
if (method != null) {
97-
return method
98-
}
99-
// let's search interfaces
100-
return findElements(
101-
packageName = { packageName },
102-
getAccessibles = { declaredMethods },
103-
nextHierarchy = { interfaces },
104-
predicate = predicate
105-
)
77+
fun JcClassOrInterface.findMethodOrNull(name: String, desc: String): JcMethod? {
78+
return lookup.method(name, desc)
10679
}
10780

10881
/**

jacodb-api/src/main/kotlin/org/jacodb/api/ext/JcCommons.kt

+1-31
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@
1818

1919
package org.jacodb.api.ext
2020

21-
import org.jacodb.api.JcAccessible
22-
import org.jacodb.api.JcAnnotated
23-
import org.jacodb.api.JcAnnotation
24-
import org.jacodb.api.JcClassOrInterface
25-
import org.jacodb.api.JcClassType
26-
import org.jacodb.api.JcClasspath
27-
import org.jacodb.api.JcMethod
28-
import org.jacodb.api.PredefinedPrimitives
29-
import org.jacodb.api.throwClassNotFound
21+
import org.jacodb.api.*
3022
import java.io.Serializable
3123
import java.lang.Cloneable
3224

@@ -98,28 +90,6 @@ internal object UnsafeHierarchyMethodComparator : Comparator<JcMethod> {
9890
}
9991
}
10092

101-
internal fun <Container : JcAccessible, Result : JcAccessible> Container.findElements(
102-
allowSearchAll: Boolean = true,
103-
packageName: Container.() -> String,
104-
classPackageName: String = packageName(this),
105-
getAccessibles: Container.() -> List<Result>,
106-
nextHierarchy: Container.() -> List<Container>?,
107-
predicate: (Result) -> Boolean
108-
): Result? {
109-
val currentPackage = packageName()
110-
return getAccessibles().firstOrNull {
111-
if (allowSearchAll) {
112-
predicate(it)
113-
} else {
114-
(it.isPublic || it.isProtected || (it.isPackagePrivate && currentPackage == classPackageName)) &&
115-
predicate(it)
116-
}
117-
} ?: nextHierarchy()?.firstNotNullOfOrNull {
118-
it.findElements(false, packageName, currentPackage, getAccessibles, nextHierarchy, predicate)
119-
}
120-
121-
}
122-
12393
fun JcAnnotated.hasAnnotation(className: String): Boolean {
12494
return annotations.any { it.matches(className) }
12595
}

jacodb-api/src/main/kotlin/org/jacodb/api/ext/JcTypes.kt

+16-38
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,7 @@
1818

1919
package org.jacodb.api.ext
2020

21-
import org.jacodb.api.JcArrayType
22-
import org.jacodb.api.JcClassType
23-
import org.jacodb.api.JcPrimitiveType
24-
import org.jacodb.api.JcRefType
25-
import org.jacodb.api.JcType
26-
import org.jacodb.api.JcTypedField
27-
import org.jacodb.api.JcTypedMethod
28-
import org.jacodb.api.throwClassNotFound
21+
import org.jacodb.api.*
2922
import java.lang.Boolean
3023
import java.lang.Byte
3124
import java.lang.Double
@@ -166,46 +159,31 @@ fun JcType.isAssignable(declaration: JcType): kotlin.Boolean {
166159

167160
/**
168161
* find field by name
162+
*
163+
* @param name field name
169164
*/
170165
fun JcClassType.findFieldOrNull(name: String): JcTypedField? {
171-
return findElements(
172-
packageName = { jcClass.packageName },
173-
getAccessibles = { declaredFields },
174-
nextHierarchy = { listOfNotNull(superType) + interfaces},
175-
) {
176-
it.name == name
177-
}
166+
return lookup.field(name)
178167
}
179168

180-
fun JcClassType.findMethodOrNull(name: String, desc: String?): JcTypedMethod? {
181-
return findMethodOrNull {
182-
it.name == name && (desc == null || it.method.description == desc)
183-
}
169+
/**
170+
* find method by name and description
171+
*
172+
* @param name method name
173+
* @param desc method description
174+
*/
175+
fun JcClassType.findMethodOrNull(name: String, desc: String): JcTypedMethod? {
176+
return lookup.method(name, desc)
184177
}
185178

186179
/**
187180
* find method by name and description
181+
*
182+
* This method doesn't support [org.jacodb.impl.features.classpaths.UnknownClasses] feature.
188183
*/
189-
fun JcClassType.findMethodOrNull(
190-
predicate: (JcTypedMethod) -> kotlin.Boolean
191-
): JcTypedMethod? {
184+
fun JcClassType.findMethodOrNull(predicate: (JcTypedMethod) -> kotlin.Boolean): JcTypedMethod? {
192185
// let's find method based on strict hierarchy
193186
// if method is not found then it's defined in interfaces
194-
return findElements(
195-
packageName = { jcClass.packageName },
196-
getAccessibles = { declaredMethods },
197-
nextHierarchy = { listOfNotNull(superType) + interfaces },
198-
predicate = predicate
199-
)
200-
// if (method != null) {
201-
// return method
202-
// }
203-
// // let's search interfaces
204-
// return findElements(
205-
// packageName = { jcClass.packageName },
206-
// getAccessibles = { declaredMethods },
207-
// nextHierarchy = { interfaces + superType?.interfaces.orEmpty() },
208-
// predicate = predicate
209-
// )
187+
return methods.firstOrNull(predicate)
210188
}
211189

jacodb-approximations/src/test/kotlin/org/jacodb/approximations/ApproximationsTest.kt

+3-10
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package org.jacodb.approximations
1818

19-
import kotlinx.coroutines.runBlocking
2019
import org.jacodb.api.JavaVersion
21-
import org.jacodb.api.JcClasspath
2220
import org.jacodb.api.cfg.*
2321
import org.jacodb.api.ext.findClass
2422
import org.jacodb.api.ext.findDeclaredFieldOrNull
@@ -29,29 +27,24 @@ import org.jacodb.approximations.target.KotlinClass
2927
import org.jacodb.impl.fs.JarLocation
3028
import org.jacodb.testing.BaseTest
3129
import org.jacodb.testing.WithDB
32-
import org.jacodb.testing.allClasspath
3330
import org.jacodb.testing.guavaLib
3431
import org.junit.jupiter.api.Assertions.*
3532
import org.junit.jupiter.api.Test
3633
import java.io.File
3734

3835
class ApproximationsTest : BaseTest() {
39-
companion object : WithDB(Approximations)
4036

41-
override val cp: JcClasspath = runBlocking {
42-
val features = listOf(Approximations)
43-
db.classpath(allClasspath, features)
44-
}
37+
companion object : WithDB(Approximations)
4538

4639
@Test
4740
fun `kotlin approximation`() {
48-
val classec = cp.findClass<KotlinClass>()
41+
val classes = cp.findClass<KotlinClass>()
4942

5043
val originalClassName = KotlinClass::class.qualifiedName!!.toOriginalName()
5144
val approximation = findApproximationByOriginOrNull(originalClassName)
5245

5346
assertNotNull(approximation)
54-
assertEquals(classec.name, findOriginalByApproximationOrNull(approximation!!.toApproximationName()))
47+
assertEquals(classes.name, findOriginalByApproximationOrNull(approximation!!.toApproximationName()))
5548
}
5649

5750
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")

0 commit comments

Comments
 (0)