Skip to content
This repository was archived by the owner on Feb 20, 2019. It is now read-only.

Commit d10ccee

Browse files
committed
Merge pull request #428 from jvican/add-specialized-tuple-support
Add support for specialized tuples
2 parents 7d1e157 + caf8730 commit d10ccee

File tree

7 files changed

+100
-72
lines changed

7 files changed

+100
-72
lines changed

core/src/main/scala/scala/pickling/generator/sourcegen.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,21 @@ private[pickling] trait SourceGenerator extends Macro with tags.FastTypeTagMacro
7070
}
7171
}
7272

73+
val classMapper = q"$picklingPath.util.ClassMapper"
7374
val unrecognizedClass = q"$errorsPath.UnrecognizedClass"
7475

7576
def genSubclassDispatch(x: SubclassDispatch): c.Tree = {
7677
val tpe = x.parent.tpe[c.universe.type](c.universe)
77-
val clazzName = newTermName("clazz")
78-
val compileTimeDispatch: List[CaseDef] = (x.subClasses map { subtpe =>
79-
val tpe = subtpe.tpe[c.universe.type](c.universe)
80-
CaseDef(Bind(clazzName, Ident(nme.WILDCARD)), q"clazz == classOf[$tpe]", createPickler(tpe, q"builder"))
81-
})(collection.breakOut)
78+
val clazz = newTermName("clazz")
79+
val compileTimeDispatch: List[CaseDef] =
80+
(x.subClasses map { subtpe =>
81+
val tpe = subtpe.tpe[c.universe.type](c.universe)
82+
CaseDef(
83+
Bind(clazz, Ident(nme.WILDCARD)),
84+
q"_root_.scala.pickling.util.ClassMapper.areSameClasses($clazz, classOf[$tpe])",
85+
createPickler(tpe, q"builder")
86+
)
87+
})(collection.breakOut)
8288

8389
val failDispatch = {
8490
val dispatcheeNames = x.subClasses.map(_.className).mkString(", ")
@@ -111,7 +117,8 @@ private[pickling] trait SourceGenerator extends Macro with tags.FastTypeTagMacro
111117
case Some(b) =>
112118
val parentTpe = x.parent.tpe[c.universe.type](c.universe)
113119
val impl = generatePickleImplFromAst(b)
114-
q"""if(classOf[$parentTpe] == picklee.getClass) $impl else $subclasses"""
120+
val checkIfParent = q"$classMapper.areSameClasses(picklee.getClass, classOf[$parentTpe])"
121+
q"""if($checkIfParent) $impl else $subclasses"""
115122
}
116123
}
117124
def genPickleEntry(op: PickleEntry): c.Tree = {

core/src/main/scala/scala/pickling/internal/DefaultPicklerRegistry.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import scala.collection.concurrent.TrieMap
44
import scala.collection.mutable
55
import scala.pickling.spi.PicklerRegistry._
66
import scala.reflect.runtime.universe.Mirror
7-
87
import scala.pickling._
8+
import scala.pickling.runtime.CustomRuntime
99
import scala.pickling.spi.{PicklerRegistry, RuntimePicklerGenerator}
10+
import scala.pickling.util.ClassMapper
1011

1112
/** Default pickle registry just uses TrieMaps and delegates behavior to a runtime pickler generator. */
1213
final class DefaultPicklerRegistry(generator: RuntimePicklerGenerator)
13-
extends PicklerRegistry with RuntimePicklerRegistry {
14+
extends PicklerRegistry with CustomRuntime {
1415

1516
type PicklerGenerator = FastTypeTag[_] => Pickler[_]
1617
type UnpicklerGenerator = FastTypeTag[_] => Unpickler[_]
@@ -20,7 +21,16 @@ final class DefaultPicklerRegistry(generator: RuntimePicklerGenerator)
2021
private val unpicklerMap: mutable.Map[String, Unpickler[_]] = new TrieMap[String, Unpickler[_]]
2122
private val unpicklerGenMap: mutable.Map[String, UnpicklerGenerator] = new TrieMap[String, UnpicklerGenerator]
2223

23-
registerRuntimePicklersAtInit()
24+
val tupleGenerators: (PicklerGen[Any], UnpicklerGen[Any]) =
25+
(tuplePicklerGenerator.asInstanceOf[PicklerGen[Any]],
26+
tupleUnpicklerGenerator.asInstanceOf[UnpicklerGen[Any]])
27+
28+
val templatesToRegister =
29+
Vector("scala.Tuple2" -> tupleGenerators) ++
30+
ClassMapper.specializedTupleNamesFor("scala.Tuple2")
31+
.map(_ -> tupleGenerators)
32+
33+
registerTemplatesAtInit(templatesToRegister)
2434

2535
override def genUnpickler(mirror: Mirror, tagKey: String)(implicit share: refs.Share): Unpickler[_] = {
2636
lookupUnpickler(tagKey) match {

core/src/main/scala/scala/pickling/internal/RuntimePicklerRegistry.scala

Lines changed: 0 additions & 61 deletions
This file was deleted.

core/src/main/scala/scala/pickling/runtime/CustomRuntime.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ trait CustomRuntime {
156156
}
157157
}
158158

159-
val tuplePicklerGenerator: PicklerUnpicklerGen[(Any, Any)] = { tpe =>
159+
val tuplePicklerGenerator: PicklerGen[(Any, Any)] = { tpe =>
160160
// TODO - Actually extract the tpe of the internal things.
161161
val tag = FastTypeTag.apply(tpe.toString)
162162
// TODO Remove this redundancy and reuse the tag above
163-
Tuple2RuntimePicklerUnpickler
163+
Tuple2RuntimePicklerUnpickler.asInstanceOf[Pickler[(Any, Any)]]
164164
}
165165

166166
val tupleUnpicklerGenerator: UnpicklerGen[(Any,Any)] = {

core/src/main/scala/scala/pickling/spi/PicklerRegistry.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ trait PicklerRegistry {
125125
*/
126126
private[pickling] def dumpStateTo(r: PicklerRegistry): Unit
127127

128+
type Templates = (PicklerGen[Any], UnpicklerGen[Any])
129+
130+
/** Register templates (also known as generators) that know how to pickle
131+
* and unpickle types at runtime without using reflection.*/
132+
final def registerTemplatesAtInit(ts: Vector[(String, Templates)]): Unit = {
133+
for((key, (pickler, unpickler)) <- ts) {
134+
registerPicklerGenerator(key, pickler)
135+
registerUnpicklerGenerator(key, unpickler)
136+
}
137+
}
138+
128139
}
129140

130141
object PicklerRegistry {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package scala.pickling.util
2+
3+
object ClassMapper {
4+
5+
val specializedTuplesTemplate =
6+
Vector(
7+
"$mcII$sp", "$mcIJ$sp", "$mcID$sp", "$mcIZ$sp", "$mcJI$sp", "$mcJJ$sp",
8+
"$mcJD$sp", "$mcJC$sp", "$mcJZ$sp", "$mcDI$sp", "$mcDJ$sp", "$mcDD$sp",
9+
"$mcDC$sp", "$mcDZ$sp", "$mcCI$sp", "$mcCJ$sp", "$mcCD$sp", "$mcCC$sp",
10+
"$mcCZ$sp", "$mcZI$sp", "$mcZJ$sp", "$mcZD$sp", "$mcZC$sp", "$mcZZ$sp"
11+
)
12+
13+
/* Map specialized classes to classes. Canonical use case: tuples.
14+
* We map classes instead of strings to check at runtime that they exist. */
15+
val specialMappingClasses: Map[Class[_], Class[_]] =
16+
mapSpecializedTuplesFor("scala.Tuple2") // add also other special cases
17+
18+
def specializedTupleNamesFor(tupleClassName: String): Vector[String] =
19+
specializedTuplesTemplate.map(tupleClassName + _)
20+
21+
def mapSpecializedTuplesFor(tupleClassName: String): Map[Class[_], Class[_]] = {
22+
val tupleClass = Class.forName(tupleClassName)
23+
specializedTupleNamesFor(tupleClassName)
24+
.map(Class.forName).map(_ -> tupleClass).toMap
25+
}
26+
27+
@inline def isSpecializedClass(specialized: Class[_], clazz: Class[_]) =
28+
specialMappingClasses.get(specialized).exists(_ == clazz)
29+
30+
def areSameClasses(clazz: Class[_], clazzT: Class[_]): Boolean =
31+
clazz == clazzT || isSpecializedClass(clazz, clazzT)
32+
33+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package scala.pickling.generation.pickler
2+
3+
import org.scalatest.FunSuite
4+
5+
import scala.pickling._
6+
import Defaults._
7+
import json._
8+
import scala.pickling.util.ClassMapper
9+
import static._
10+
11+
class Tuple2Pickler extends FunSuite {
12+
13+
test("pickle/unpickle (Int, String)") {
14+
val t = (1, "")
15+
val t2 = t.pickle.unpickle[(Int, String)]
16+
assert(t === t2)
17+
}
18+
19+
test("pickle/unpickle any specialized tuple like (Int, Int)") {
20+
val t = (1, 2)
21+
val clz = classOf[(Int, Int)]
22+
val clzT = t.getClass
23+
println(ClassMapper.areSameClasses(clzT, clz))
24+
val t2 = t.pickle.unpickle[(Int, Int)]
25+
assert(t === t2)
26+
}
27+
28+
}

0 commit comments

Comments
 (0)