All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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