![JAR search and dependency download from the Maven repository](/logo.png)
org.jacodb.impl.JcClasspathImpl.kt Maven / Gradle / Ivy
Show all versions of jacodb-core Show documentation
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jacodb.impl
import kotlinx.coroutines.*
import org.jacodb.api.*
import org.jacodb.api.JcClasspathExtFeature.JcResolvedClassResult
import org.jacodb.api.JcClasspathExtFeature.JcResolvedTypeResult
import org.jacodb.api.ext.toType
import org.jacodb.impl.bytecode.JcClassOrInterfaceImpl
import org.jacodb.impl.features.JcFeatureEventImpl
import org.jacodb.impl.features.JcFeaturesChain
import org.jacodb.impl.features.classpaths.AbstractJcResolvedResult.JcResolvedClassResultImpl
import org.jacodb.impl.features.classpaths.AbstractJcResolvedResult.JcResolvedTypeResultImpl
import org.jacodb.impl.features.classpaths.JcUnknownClass
import org.jacodb.impl.features.classpaths.UnknownClasses
import org.jacodb.impl.features.classpaths.isResolveAllToUnknown
import org.jacodb.impl.fs.ClassSourceImpl
import org.jacodb.impl.types.JcArrayTypeImpl
import org.jacodb.impl.types.JcClassTypeImpl
import org.jacodb.impl.types.substition.JcSubstitutor
import org.jacodb.impl.vfs.ClasspathVfs
import org.jacodb.impl.vfs.GlobalClassesVfs
class JcClasspathImpl(
private val locationsRegistrySnapshot: LocationsRegistrySnapshot,
override val db: JcDatabaseImpl,
override val features: List,
globalClassVFS: GlobalClassesVfs
) : JcClasspath {
override val locations: List = locationsRegistrySnapshot.locations.mapNotNull { it.jcLocation }
override val registeredLocations: List = locationsRegistrySnapshot.locations
private val classpathVfs = ClasspathVfs(globalClassVFS, locationsRegistrySnapshot)
private val featuresChain = run{
val strictFeatures = features.filter { it !is UnknownClasses }
val hasUnknownClasses = strictFeatures.size != features.size
JcFeaturesChain(strictFeatures + listOfNotNull(JcClasspathFeatureImpl(), UnknownClasses.takeIf { hasUnknownClasses }) )
}
override suspend fun refreshed(closeOld: Boolean): JcClasspath {
return db.new(this).also {
if (closeOld) {
close()
}
}
}
override fun findClassOrNull(name: String): JcClassOrInterface? {
return featuresChain.call {
it.tryFindClass(this, name)
}?.clazz
}
override fun typeOf(
jcClass: JcClassOrInterface,
nullability: Boolean?,
annotations: List
): JcRefType {
return JcClassTypeImpl(
this,
jcClass.name,
jcClass.outerClass?.toType() as? JcClassTypeImpl,
JcSubstitutor.empty,
nullability,
annotations
)
}
override fun arrayTypeOf(elementType: JcType, nullability: Boolean?, annotations: List): JcArrayType {
return JcArrayTypeImpl(elementType, nullability, annotations)
}
override fun toJcClass(source: ClassSource): JcClassOrInterface {
return JcClassOrInterfaceImpl(this, source, featuresChain)
}
override fun findTypeOrNull(name: String): JcType? {
return featuresChain.call {
it.tryFindType(this, name)
}?.type
}
override suspend fun execute(task: T): T {
val locations = registeredLocations.filter { task.shouldProcess(it) }
task.before(this)
withContext(Dispatchers.IO) {
val parentScope = this
locations.map {
async {
val sources = db.persistence.findClassSources(db, it)
.takeIf { it.isNotEmpty() } ?: it.jcLocation?.classes?.map { entry ->
ClassSourceImpl(location = it, className = entry.key, byteCode = entry.value)
} ?: emptyList()
sources.forEach {
if (parentScope.isActive && task.shouldProcess(it)) {
task.process(it, this@JcClasspathImpl)
}
}
}
}.joinAll()
}
task.after(this)
return task
}
override fun findClasses(name: String): Set {
return featuresChain.features.filterIsInstance().flatMap { feature ->
feature.findClasses(this, name).orEmpty()
}.toSet()
}
override fun isInstalled(feature: JcClasspathFeature): Boolean {
return featuresChain.features.contains(feature)
}
override fun close() {
locationsRegistrySnapshot.close()
}
private inner class JcClasspathFeatureImpl : JcClasspathExtFeature {
override fun tryFindClass(classpath: JcClasspath, name: String): JcResolvedClassResult? {
val source = classpathVfs.firstClassOrNull(name)
val jcClass = source?.let { toJcClass(it.source) }
?: db.persistence.findClassSourceByName(classpath, name)?.let {
toJcClass(it)
}
if (jcClass == null && isResolveAllToUnknown) {
return null
}
return JcResolvedClassResultImpl(name, jcClass)
}
override fun tryFindType(classpath: JcClasspath, name: String): JcResolvedTypeResult? {
if (name.endsWith("[]")) {
val targetName = name.removeSuffix("[]")
return JcResolvedTypeResultImpl(name,
findTypeOrNull(targetName)?.let { JcArrayTypeImpl(it, true) }
)
}
val predefined = PredefinedPrimitives.of(name, classpath)
if (predefined != null) {
return JcResolvedTypeResultImpl(name, predefined)
}
return when (val clazz = findClassOrNull(name)) {
null -> JcResolvedTypeResultImpl(name, null)
is JcUnknownClass -> null // delegating to UnknownClass feature
else -> JcResolvedTypeResultImpl(name, typeOf(clazz))
}
}
override fun findClasses(classpath: JcClasspath, name: String): List {
val vfsClasses = classpathVfs.findClassNodes(name).map { toJcClass(it.source) }
val persistedClasses = db.persistence.findClassSources(classpath, name).map { toJcClass(it) }
return buildSet {
addAll(vfsClasses)
addAll(persistedClasses)
}.toList()
}
override fun event(result: Any): JcFeatureEvent {
return JcFeatureEventImpl(this, result)
}
}
}