Skip to content
This repository was archived by the owner on Mar 24, 2025. It is now read-only.

Commit b4fccc0

Browse files
add synchronized (#17)
1 parent 1c60ce0 commit b4fccc0

File tree

3 files changed

+165
-1
lines changed

3 files changed

+165
-1
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ The `@builder` used to generate builder pattern for Scala classes.
6969

7070
- Note
7171
- Support `case class` / `class`.
72-
- It can be used with `@toString`. But it needs to be put in the back.
72+
- It can be used with `@toString`. But `@builder` needs to be put in the back.
7373
- If there is no companion object, one will be generated to store the `builder` method and `Builder` class.
7474
- IDE support is not very good, a red prompt will appear, but the compilation is OK.
7575

@@ -122,6 +122,42 @@ object TestClass1 extends scala.AnyRef {
122122
}
123123
```
124124

125+
## @synchronized
126+
127+
The `@synchronized` is a more convenient and flexible synchronous annotation.
128+
129+
- Note
130+
- `lockedName` The name of custom lock obj.
131+
- Support static and instance methods.
132+
- It can customize the lock object, and the default is `this`.
133+
134+
- Example
135+
136+
```scala
137+
138+
private final val obj = new Object
139+
140+
@synchronized(lockedName = "obj") // The default is this. If you fill in a non existent field name, the compilation will fail.
141+
def getStr3(k: Int): String = {
142+
k + ""
143+
}
144+
145+
// or
146+
@synchronized //use this
147+
def getStr(k: Int): String = {
148+
k + ""
149+
}
150+
```
151+
152+
Compiler intermediate code:
153+
154+
```scala
155+
// Note that it will not judge whether synchronized already exists, so if synchronized already exists, it will be used twice.
156+
// For example `def getStr(k: Int): String = this.synchronized(this.synchronized(k.$plus("")))
157+
// It is not sure whether it will be optimized at the bytecode level.
158+
def getStr(k: Int): String = this.synchronized(k.$plus(""))
159+
```
160+
125161
# How to use
126162

127163
Add library dependency
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package io.github.dreamylost
2+
3+
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
4+
import scala.language.experimental.macros
5+
import scala.reflect.macros.whitebox
6+
7+
/**
8+
*
9+
* @author 梦境迷离
10+
* @param lockedName The name of custom lock obj.
11+
* @param verbose Whether to enable detailed log.
12+
* @since 2021/6/24
13+
* @version 1.0
14+
*/
15+
@compileTimeOnly("enable macro to expand macro annotations")
16+
final class synchronized(
17+
verbose: Boolean = false,
18+
lockedName: String = "this"
19+
) extends StaticAnnotation {
20+
def macroTransform(annottees: Any*): Any = macro synchronizedMacro.impl
21+
}
22+
23+
object synchronizedMacro {
24+
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
25+
import c.universe._
26+
27+
val args: (Boolean, String) = c.prefix.tree match {
28+
case q"new synchronized(verbose=$verbose, lockedName=$lock)" => (c.eval[Boolean](c.Expr(verbose)), c.eval[String](c.Expr(lock)))
29+
case q"new synchronized(lockedName=$lock)" => (false, c.eval[String](c.Expr(lock)))
30+
case q"new synchronized()" => (false, "this")
31+
case _ => c.abort(c.enclosingPosition, "unexpected annotation pattern!")
32+
}
33+
34+
c.info(c.enclosingPosition, s"annottees: $annottees", force = args._1)
35+
36+
val resTree = annottees map (_.tree) match {
37+
// Match a method, and expand.
38+
case _@ q"$modrs def $tname[..$tparams](...$paramss): $tpt = $expr" :: _ =>
39+
if (args._2 != null) {
40+
if (args._2 == "this") {
41+
q"""def $tname[..$tparams](...$paramss): $tpt = ${This(TypeName(""))}.synchronized { $expr }"""
42+
} else {
43+
q"""def $tname[..$tparams](...$paramss): $tpt = ${TermName(args._2)}.synchronized { $expr }"""
44+
}
45+
} else {
46+
c.abort(c.enclosingPosition, "Invalid args, lockName cannot be a null!")
47+
}
48+
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: not a method")
49+
}
50+
// Print the ast
51+
c.info(
52+
c.enclosingPosition,
53+
"\n###### Expanded macro ######\n" + resTree.toString() + "\n###### Expanded macro ######\n",
54+
force = args._1
55+
)
56+
c.Expr[Any](resTree)
57+
}
58+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package io.github.dreamylost
2+
3+
import org.scalatest.{ FlatSpec, Matchers }
4+
5+
/**
6+
*
7+
* @author 梦境迷离
8+
* @since 2021/6/24
9+
* @version 1.0
10+
*/
11+
class Synchronized extends FlatSpec with Matchers {
12+
13+
"synchronized1" should "is ok at class" in {
14+
@synchronized
15+
def getStr(k: Int): String = {
16+
k + ""
17+
}
18+
"""@synchronized
19+
def getStr(k: Int): String = {
20+
k + ""
21+
}
22+
""" should compile
23+
24+
@synchronized
25+
def getStr2(k: Int): String = {
26+
k + ""
27+
}
28+
"""@synchronized
29+
def getStr2(k: Int) = {
30+
k + ""
31+
}
32+
""" should compile
33+
}
34+
35+
"synchronized2" should "is ok by custom obj" in {
36+
37+
val obj = new Object
38+
39+
@synchronized(lockedName = "obj")
40+
def getStr3(k: Int): String = {
41+
k + ""
42+
}
43+
"""
44+
@synchronized(lockedName = "obj")
45+
def getStr3(k: Int) = {
46+
k + ""
47+
}
48+
""" should compile
49+
50+
object TestObject {
51+
// def getStr(k: Int): String = this.synchronized(k.$plus(""))
52+
// def getStr(k: Int): String = this.synchronized(this.synchronized(k.$plus("")))
53+
@synchronized
54+
def getStr(k: Int): String = {
55+
this.synchronized(k + "")
56+
}
57+
}
58+
59+
"""
60+
@synchronized(lockedName = "obj")
61+
class A
62+
""" shouldNot compile
63+
64+
"""
65+
@synchronized(lockedName = "obj")
66+
val s = "1"
67+
""" shouldNot compile
68+
}
69+
70+
}

0 commit comments

Comments
 (0)