@@ -2,6 +2,47 @@ overlay[local?]
22module ;
33
44import java
5+ import semmle.code.java.frameworks.Mockito
6+
7+ class LockType extends RefType {
8+ LockType ( ) {
9+ this .getAMethod ( ) .hasName ( "lock" ) and
10+ this .getAMethod ( ) .hasName ( "unlock" )
11+ }
12+
13+ Method getLockMethod ( ) {
14+ result .getDeclaringType ( ) = this and
15+ result .hasName ( [ "lock" , "lockInterruptibly" , "tryLock" ] )
16+ }
17+
18+ Method getUnlockMethod ( ) {
19+ result .getDeclaringType ( ) = this and
20+ result .hasName ( "unlock" )
21+ }
22+
23+ Method getIsHeldByCurrentThreadMethod ( ) {
24+ result .getDeclaringType ( ) = this and
25+ result .hasName ( "isHeldByCurrentThread" )
26+ }
27+
28+ MethodCall getLockAccess ( ) {
29+ result .getMethod ( ) = this .getLockMethod ( ) and
30+ // Not part of a Mockito verification call
31+ not result instanceof MockitoVerifiedMethodCall
32+ }
33+
34+ MethodCall getUnlockAccess ( ) {
35+ result .getMethod ( ) = this .getUnlockMethod ( ) and
36+ // Not part of a Mockito verification call
37+ not result instanceof MockitoVerifiedMethodCall
38+ }
39+
40+ MethodCall getIsHeldByCurrentThreadAccess ( ) {
41+ result .getMethod ( ) = this .getIsHeldByCurrentThreadMethod ( ) and
42+ // Not part of a Mockito verification call
43+ not result instanceof MockitoVerifiedMethodCall
44+ }
45+ }
546
647/**
748 * Holds if `e` is synchronized by a local synchronized statement `sync` on the variable `v`.
@@ -49,3 +90,123 @@ class SynchronizedCallable extends Callable {
4990 )
5091 }
5192}
93+
94+ /**
95+ * This module provides predicates, chiefly `locallyMonitors`, to check if a given expression is synchronized on a specific monitor.
96+ */
97+ module Monitors {
98+ /**
99+ * A monitor is any object that is used to synchronize access to a shared resource.
100+ * This includes locks as well as variables used in synchronized blocks (including `this`).
101+ */
102+ newtype TMonitor =
103+ /** Either a lock or a variable used in a synchronized block. */
104+ TVariableMonitor ( Variable v ) {
105+ v .getType ( ) instanceof LockType or locallySynchronizedOn ( _, _, v )
106+ } or
107+ /** An instance reference used as a monitor. */
108+ TInstanceMonitor ( RefType thisType ) { locallySynchronizedOnThis ( _, thisType ) } or
109+ /** A class used as a monitor. */
110+ TClassMonitor ( RefType classType ) { locallySynchronizedOnClass ( _, classType ) }
111+
112+ /**
113+ * A monitor is any object that is used to synchronize access to a shared resource.
114+ * This includes locks as well as variables used in synchronized blocks (including `this`).
115+ */
116+ class Monitor extends TMonitor {
117+ /** Gets the location of this monitor. */
118+ abstract Location getLocation ( ) ;
119+
120+ /** Gets a textual representation of this element. */
121+ abstract string toString ( ) ;
122+ }
123+
124+ /**
125+ * A variable used as a monitor.
126+ * The variable is either a lock or is used in a synchronized block.
127+ * E.g `synchronized (m) { ... }` or `m.lock();`
128+ */
129+ class VariableMonitor extends Monitor , TVariableMonitor {
130+ override Location getLocation ( ) { result = this .getVariable ( ) .getLocation ( ) }
131+
132+ override string toString ( ) { result = "VariableMonitor(" + this .getVariable ( ) .toString ( ) + ")" }
133+
134+ /** Gets the variable being used as a monitor. */
135+ Variable getVariable ( ) { this = TVariableMonitor ( result ) }
136+ }
137+
138+ /**
139+ * An instance reference used as a monitor.
140+ * Either via `synchronized (this) { ... }` or by marking a non-static method as `synchronized`.
141+ */
142+ class InstanceMonitor extends Monitor , TInstanceMonitor {
143+ override Location getLocation ( ) { result = this .getThisType ( ) .getLocation ( ) }
144+
145+ override string toString ( ) { result = "InstanceMonitor(" + this .getThisType ( ) .toString ( ) + ")" }
146+
147+ /** Gets the instance reference being used as a monitor. */
148+ RefType getThisType ( ) { this = TInstanceMonitor ( result ) }
149+ }
150+
151+ /**
152+ * A class used as a monitor.
153+ * This is achieved by marking a static method as `synchronized`.
154+ */
155+ class ClassMonitor extends Monitor , TClassMonitor {
156+ override Location getLocation ( ) { result = this .getClassType ( ) .getLocation ( ) }
157+
158+ override string toString ( ) { result = "ClassMonitor(" + this .getClassType ( ) .toString ( ) + ")" }
159+
160+ /** Gets the class being used as a monitor. */
161+ RefType getClassType ( ) { this = TClassMonitor ( result ) }
162+ }
163+
164+ /** Holds if the expression `e` is synchronized on the monitor `m`. */
165+ predicate locallyMonitors ( Expr e , Monitor m ) {
166+ exists ( Variable v | v = m .( VariableMonitor ) .getVariable ( ) |
167+ locallyLockedOn ( e , v )
168+ or
169+ locallySynchronizedOn ( e , _, v )
170+ )
171+ or
172+ locallySynchronizedOnThis ( e , m .( InstanceMonitor ) .getThisType ( ) )
173+ or
174+ locallySynchronizedOnClass ( e , m .( ClassMonitor ) .getClassType ( ) )
175+ }
176+
177+ /** Holds if `localLock` refers to `lock`. */
178+ predicate represents ( Field lock , Variable localLock ) {
179+ lock .getType ( ) instanceof LockType and
180+ (
181+ localLock = lock
182+ or
183+ localLock .getInitializer ( ) = lock .getAnAccess ( )
184+ )
185+ }
186+
187+ /** Gets the control flow node that must dominate `e` when `e` is synchronized on a lock. */
188+ ControlFlowNode getNodeToBeDominated ( Expr e ) {
189+ // If `e` is the LHS of an assignment, use the control flow node for the assignment
190+ exists ( Assignment asgn | asgn .getDest ( ) = e | result = asgn .getControlFlowNode ( ) )
191+ or
192+ // if `e` is not the LHS of an assignment, use the default control flow node
193+ not exists ( Assignment asgn | asgn .getDest ( ) = e ) and
194+ result = e .getControlFlowNode ( )
195+ }
196+
197+ /** Holds if `e` is synchronized on the `Lock` `lock` by a locking call. */
198+ predicate locallyLockedOn ( Expr e , Field lock ) {
199+ lock .getType ( ) instanceof LockType and
200+ exists ( Variable localLock , MethodCall lockCall , MethodCall unlockCall |
201+ represents ( lock , localLock ) and
202+ lockCall .getQualifier ( ) = localLock .getAnAccess ( ) and
203+ lockCall .getMethod ( ) = lock .getType ( ) .( LockType ) .getLockMethod ( ) and
204+ unlockCall .getQualifier ( ) = localLock .getAnAccess ( ) and
205+ unlockCall .getMethod ( ) = lock .getType ( ) .( LockType ) .getUnlockMethod ( )
206+ |
207+ dominates ( lockCall .getControlFlowNode ( ) , unlockCall .getControlFlowNode ( ) ) and
208+ dominates ( lockCall .getControlFlowNode ( ) , getNodeToBeDominated ( e ) ) and
209+ postDominates ( unlockCall .getControlFlowNode ( ) , getNodeToBeDominated ( e ) )
210+ )
211+ }
212+ }
0 commit comments