
org.opalj.br.analyses.DeclaredMethods.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package analyses
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantReadWriteLock
import org.opalj.log.LogContext
import org.opalj.log.OPALLogger.info
import org.opalj.br.MethodDescriptor.SignaturePolymorphicMethodBoolean
import org.opalj.br.MethodDescriptor.SignaturePolymorphicMethodObject
import org.opalj.br.MethodDescriptor.SignaturePolymorphicMethodVoid
import org.opalj.br.ObjectType.MethodHandle
import org.opalj.br.ObjectType.VarHandle
import org.opalj.br.analyses.DeclaredMethodsKey.MethodContext
import org.opalj.br.analyses.DeclaredMethodsKey.MethodContextQuery
/**
* The set of all [[org.opalj.br.DeclaredMethod]]s (potentially used by the property store).
*
* @author Dominik Helm
*/
class DeclaredMethods(
private[this] val p: SomeProject,
// We need concurrent, mutable maps here, as VirtualDeclaredMethods may be added when they
// are queried. This can result in DeclaredMethods added for a type not yet seen, too (e.g.
// methods on type Object when not analyzing the JDK.
private[this] val data: ConcurrentHashMap[ReferenceType, ConcurrentHashMap[MethodContext, DeclaredMethod]],
private[this] var id2method: Array[DeclaredMethod],
private[this] var idCounter: Int
) {
private[this] final val lock = new ReentrantReadWriteLock()
private var extensionSize = 1000
/**
* A (possibly stale, i.e., too low) view of the number of declared methods
*/
def _UNSAFE_size: Int = idCounter
def apply(
declaredType: ObjectType,
packageName: String,
runtimeType: ObjectType,
name: String,
descriptor: MethodDescriptor
): DeclaredMethod = {
val dmSet = data.computeIfAbsent(runtimeType, _ => new ConcurrentHashMap)
val context = new MethodContextQuery(p, declaredType, packageName, name, descriptor)
var method = dmSet.get(context)
if (method != null) return method;
if (p.isSignaturePolymorphic(runtimeType, name, descriptor)) {
val signaturePolymorphicMethodDescriptor =
if (runtimeType eq VarHandle) {
if (name == "compareAndSet" || name.startsWith("weak"))
SignaturePolymorphicMethodBoolean
else if (name.startsWith("set"))
SignaturePolymorphicMethodVoid
else if (name.startsWith("get") || name.startsWith("compare"))
SignaturePolymorphicMethodObject
else
throw new IllegalArgumentException(
s"Unexpected signature polymorphic method $name"
)
} else if ((runtimeType eq MethodHandle) &&
(name == "invoke" || name == "invokeExact")) {
SignaturePolymorphicMethodObject
} else
throw new IllegalArgumentException(
s"Unexpected signature polymorphic method $name"
)
method = dmSet.get(
new MethodContextQuery(
p,
declaredType,
packageName,
name,
signaturePolymorphicMethodDescriptor
)
)
if (method != null) return method;
}
// Package private methods can be invoked from other packages if they override a public
// method. Thus, try a query with the receiver's package name.
method = dmSet.get(
new MethodContextQuery(p, runtimeType, runtimeType.packageName, name, descriptor)
)
if (method != null) return method;
// In case of an unseen method, compute id
lock.writeLock().lock()
try {
if (!dmSet.contains(context)) {
val vm = new VirtualDeclaredMethod(runtimeType, name, descriptor, idCounter)
idCounter += 1
dmSet.put(MethodContext(p, runtimeType, "", name, descriptor, false), vm)
if (id2method.size <= vm.id) {
implicit val logContext: LogContext = p.logContext
info(
"project",
"too many virtual declared methods; extended the underlying array"
)
val id2methodExt = new Array[DeclaredMethod](id2method.length + extensionSize)
extensionSize = Math.min(extensionSize * 2, 32000)
Array.copy(id2method, 0, id2methodExt, 0, id2method.length)
id2method = id2methodExt
}
id2method(vm.id) = vm
}
} finally {
lock.writeLock().unlock()
}
method = dmSet.get(context)
assert(method ne null)
method
}
def apply(method: Method): DefinedMethod = {
val classType = method.classFile.thisType
data.get(classType).get(MethodContext(p, classType, method)).asInstanceOf[DefinedMethod]
}
def apply(methodId: Int): DeclaredMethod = {
lock.readLock().lock()
try {
id2method(methodId)
} finally {
lock.readLock().unlock()
}
}
def get(methodId: Int): Option[DeclaredMethod] = {
lock.readLock().lock()
try {
if (methodId < id2method.length) Some(id2method(methodId))
else None
} finally {
lock.readLock().unlock()
}
}
def declaredMethods: Iterator[DeclaredMethod] = {
import scala.jdk.CollectionConverters._
// Thread-safe as .values() creates a view of the current state
data.values().asScala.iterator.flatMap { _.values().asScala }
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy