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

org.opalj.br.fpcf.properties.Context.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package fpcf
package properties

import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantReadWriteLock
import org.opalj.br.analyses.DeclaredMethods
import org.opalj.br.analyses.DeclaredMethodsKey
import org.opalj.br.analyses.ProjectInformationKey
import org.opalj.br.analyses.ProjectInformationKeys
import org.opalj.br.analyses.SomeProject

import scala.collection.mutable

/**
 * Provides the context in which a method was invoked or an object was allocated.
 *
 * @author Dominik Helm
 */
trait Context {
    val hasContext: Boolean = true

    /** The method itself */
    def method: DeclaredMethod

    /** An identifier for the context */
    def id: Int
}

object Context {
    def unapply(context: Context): Option[DeclaredMethod] = {
        if (context.hasContext) Some(context.method)
        else None
    }
}

/**
 *  Represents unknown contexts.
 */
case object NoContext extends Context {
    override val hasContext: Boolean = false

    override def method: DeclaredMethod = throw new UnsupportedOperationException()

    val id: Int = -1
}

/**
 * A simple context that provides the bare minumum for context-insensitive analyses.
 */
case class SimpleContext private[properties] (method: DeclaredMethod) extends Context {
    override def id: Int = method.id
}

object SimpleContextsKey extends ProjectInformationKey[SimpleContexts, Nothing] {

    override def requirements(project: SomeProject): ProjectInformationKeys =
        Seq(DeclaredMethodsKey)

    override def compute(p: SomeProject): SimpleContexts = {
        new SimpleContexts(p.get(DeclaredMethodsKey))
    }

}

class SimpleContexts private[properties] (declaredMethods: DeclaredMethods) {

    @volatile private var id2Context = new Array[SimpleContext](declaredMethods._UNSAFE_size)

    def apply(method: DeclaredMethod): SimpleContext = {
        val id = method.id
        if (id < id2Context.length) {
            var context = id2Context(id)
            if (context ne null) context
            else {
                synchronized {
                    context = id2Context(id)
                    if (context ne null) context
                    else {
                        val newContext = SimpleContext(method)
                        id2Context(id) = newContext
                        newContext
                    }
                }
            }
        } else {
            synchronized {
                if (id < id2Context.length) {
                    val context = id2Context(id)
                    if (context ne null) context
                    else {
                        val newContext = SimpleContext(method)
                        id2Context(id) = newContext
                        newContext
                    }
                } else {
                    val newContext = SimpleContext(method)
                    val newMap = java.util.Arrays.copyOf(
                        id2Context, Math.max(declaredMethods._UNSAFE_size, id + 1)
                    )
                    newMap(id) = newContext
                    id2Context = newMap
                    newContext
                }
            }
        }
    }
}

/**
 * A context that includes a call string
 */
class CallStringContext private[properties] (
        val id:         Int,
        val method:     DeclaredMethod,
        val callString: List[(DeclaredMethod, Int)]
) extends Context {
    override def toString: String = {
        s"CallStringContext($method, $callString)"
    }
}

object CallStringContextsKey extends ProjectInformationKey[CallStringContexts, Nothing] {

    override def requirements(project: SomeProject): ProjectInformationKeys =
        Seq(DeclaredMethodsKey)

    override def compute(p: SomeProject): CallStringContexts = {
        new CallStringContexts()
    }

}

class CallStringContexts {

    @volatile private var id2Context = new Array[CallStringContext](32768)
    private val context2id = new mutable.HashMap[(DeclaredMethod, List[(DeclaredMethod, Int)]), CallStringContext]()

    private val nextId = new AtomicInteger(1)
    private val rwLock = new ReentrantReadWriteLock()

    def apply(id: Int): CallStringContext = {
        id2Context(id)
    }

    def apply(
        method:     DeclaredMethod,
        callString: List[(DeclaredMethod, Int)]
    ): CallStringContext = {
        val key = (method, callString)

        val readLock = rwLock.readLock()
        readLock.lock()
        try {
            val contextO = context2id.get(key)
            if (contextO.isDefined) {
                return contextO.get;
            }
        } finally {
            readLock.unlock()
        }

        val writeLock = rwLock.writeLock()
        writeLock.lock()
        try {
            val contextO = context2id.get(key)
            if (contextO.isDefined) {
                return contextO.get;
            }

            val context = new CallStringContext(nextId.getAndIncrement(), method, callString)
            context2id.put(key, context)
            val curMap = id2Context
            if (context.id < curMap.length) {
                curMap(context.id) = context
            } else {
                val newMap = java.util.Arrays.copyOf(curMap, curMap.length * 2)
                newMap(context.id) = context
                id2Context = newMap
            }
            context
        } finally {
            writeLock.unlock()
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy