Skip to content

Commit

Permalink
Make the entire API thread-safe.
Browse files Browse the repository at this point in the history
On the condition that the `ClasspathEntry`s used to create the
`Context` are thread-safe.
  • Loading branch information
sjrd committed Dec 22, 2023
1 parent 227c2ef commit 057d3ae
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ object ClasspathLoaders:
* to create a [[Contexts.Context]]. The latter gives semantic access to all
* the definitions on the classpath.
*
* The entries of the resulting [[Classpaths.Classpath]] can be considered
* thread-safe, since the JavaScript environment is always single-threaded.
*
* @note the resulting [[Classpaths.ClasspathEntry ClasspathEntry]] entries of
* the returned [[Classpaths.Classpath]] correspond to the elements of `classpath`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ object ClasspathLoaders {
* to create a [[Contexts.Context]]. The latter gives semantic access to all
* the definitions on the classpath.
*
* The entries of the resulting [[Classpaths.Classpath]] are all guaranteed
* to be thread-safe.
*
* @note the resulting [[Classpaths.ClasspathEntry ClasspathEntry]] entries of
* the returned [[Classpaths.Classpath]] correspond to the elements of `classpath`.
*/
Expand Down
16 changes: 8 additions & 8 deletions tasty-query/shared/src/main/scala/tastyquery/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import tastyquery.Utils.*

object Annotations:
final class Annotation(val tree: TermTree):
private var mySymbol: Memo[ClassSymbol] = uninitializedMemo
private var mySafeSymbol: Memo[Option[ClassSymbol]] = uninitializedMemo
private var myArguments: Memo[List[TermTree]] = uninitializedMemo
private val mySymbol: Memo[ClassSymbol] = uninitializedMemo
private val mySafeSymbol: Memo[Option[ClassSymbol]] = uninitializedMemo
private val myArguments: Memo[List[TermTree]] = uninitializedMemo

/** The annotation class symbol. */
def symbol(using Context): ClassSymbol =
memoized2(mySymbol, mySymbol = _) {
memoized2(mySymbol) {
computeAnnotSymbol(tree)
} { computed =>
initializeMemo[Option[ClassSymbol]](mySafeSymbol = _, Some(computed))
initializeMemo(mySafeSymbol, Some(computed))
}
end symbol

Expand All @@ -31,10 +31,10 @@ object Annotations:
* If the class of this annotation cannot be successfully resolved, returns `false`.
*/
private[tastyquery] def safeHasSymbol(cls: ClassSymbol)(using Context): Boolean =
val safeSymbol = memoized2(mySafeSymbol, mySafeSymbol = _) {
val safeSymbol = memoized2(mySafeSymbol) {
computeSafeAnnotSymbol(tree)
} { computed =>
computed.foreach(sym => initializeMemo[ClassSymbol](mySymbol = _, sym))
computed.foreach(sym => initializeMemo(mySymbol, sym))
}

safeSymbol.contains(cls)
Expand All @@ -56,7 +56,7 @@ object Annotations:
* `NamedArg`s are not visible with this method. They are replaced by
* their right-hand-side.
*/
def arguments: List[TermTree] = memoized(myArguments, myArguments = _) {
def arguments: List[TermTree] = memoized(myArguments) {
computeAnnotArguments(tree)
}

Expand Down
13 changes: 12 additions & 1 deletion tasty-query/shared/src/main/scala/tastyquery/Classpaths.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ object Classpaths:
* All the methods of `ClasspathEntry` and its components may throw
* `java.io.IOException`s to indicate I/O errors.
*
* A `ClasspathEntry` is encouraged to be thread-safe, along with all its
* components, but it is not a strong requirement. Implementations that are
* thread-safe should be documented as such. [[Contexts.Context]]s created
* only from thread-safe `ClasspathEntry`s are thread-safe themselves.
*
* Implementations of this class are encouraged to define a `toString()`
* method that helps identifying the entry for debugging purposes.
*/
Expand Down Expand Up @@ -99,16 +104,21 @@ object Classpaths:
def readClassFileBytes(): IArray[Byte]
end ClassData

/** In-memory representation of classpath entries. */
/** In-memory representation of classpath entries.
*
* In-memory classpath entries are thread-safe.
*/
object InMemory:
import Classpaths as generic

/** A thread-safe, immutable classpath entry. */
final class ClasspathEntry(debugString: String, val packages: List[PackageData]) extends generic.ClasspathEntry:
override def toString(): String = debugString

def listAllPackages(): List[generic.PackageData] = packages
end ClasspathEntry

/** A thread-safe, immutable package information within a classpath entry. */
final class PackageData(debugString: String, val dotSeparatedName: String, val classes: List[ClassData])
extends generic.PackageData:
private lazy val byBinaryName = classes.map(c => c.binaryName -> c).toMap
Expand All @@ -120,6 +130,7 @@ object Classpaths:
def getClassDataByBinaryName(binaryName: String): Option[generic.ClassData] = byBinaryName.get(binaryName)
end PackageData

/** A thread-safe, immutable class information within a classpath entry. */
final class ClassData(
debugString: String,
val binaryName: String,
Expand Down
6 changes: 5 additions & 1 deletion tasty-query/shared/src/main/scala/tastyquery/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ object Contexts {

/** Factory methods for [[Context]]. */
object Context:
/** Creates a new [[Context]] for the given [[Classpaths.Classpath]]. */
/** Creates a new [[Context]] for the given [[Classpaths.Classpath]].
*
* If all the [[Classpaths.ClasspathEntry ClasspathEntries]] in the classpath
* are thread-safe, then the resulting [[Context]] is thread-safe.
*/
def initialize(classpath: Classpath): Context =
val classloader = Loader(classpath)
val ctx = new Context(classloader)
Expand Down
Loading

0 comments on commit 057d3ae

Please sign in to comment.