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

Commit 64ebb7b

Browse files
committed
Merge pull request #100 from scala/issue/75
Fix #75 - Runtime exception when unpickling Array of Tuple
2 parents 7118a68 + b9e986a commit 64ebb7b

File tree

3 files changed

+75
-15
lines changed

3 files changed

+75
-15
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package scala.pickling.internal
2+
3+
object AppliedType {
4+
// the delimiters in an applied type
5+
private val delims = List(',', '[', ']')
6+
7+
/* Parse an applied type.
8+
*
9+
* @param s the string that is parsed
10+
* @return a pair with the parsed applied type and the remaining string.
11+
*/
12+
def parse(s: String): (AppliedType, String) = {
13+
// shape of `s`: fqn[at_1, ..., at_n]
14+
val (typename, rem) = s.span(!delims.contains(_))
15+
16+
if (rem.isEmpty || rem.startsWith(",") || rem.startsWith("]")) {
17+
(AppliedType(typename, List()), rem)
18+
} else { // parse type arguments
19+
var typeArgs = List[AppliedType]()
20+
var remaining = rem
21+
22+
while (remaining.startsWith("[") || remaining.startsWith(",")) {
23+
remaining = remaining.substring(1)
24+
val (nextAppliedType, rem) = parse(remaining)
25+
typeArgs = typeArgs :+ nextAppliedType
26+
remaining = rem
27+
}
28+
29+
(AppliedType(typename, typeArgs), if (remaining.startsWith("]")) remaining.substring(1) else remaining)
30+
}
31+
}
32+
33+
}
34+
35+
/**
36+
* Simple representation of an applied type. Used for reading pickled types.
37+
*/
38+
case class AppliedType(typename: String, typeargs: List[AppliedType]) {
39+
override def toString =
40+
typename + (if (typeargs.isEmpty) "" else typeargs.mkString("[", ",", "]"))
41+
}

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

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,17 @@ package object internal {
4646
// TODO: find out why typeFromString is called repeatedly for scala.Predef.String (at least in the evactor1 bench)
4747
if (typeFromStringCache.contains(stpe)) typeFromStringCache(stpe)
4848
else {
49-
val result = {
50-
val (ssym, stargs) = {
51-
val Pattern = """^(.*?)(\[(.*?)\])?$""".r
52-
def fail() = throw new PicklingException(s"fatal: cannot unpickle $stpe")
53-
stpe match {
54-
case Pattern("", _, _) => fail()
55-
case Pattern(sym, _, null) => (sym, Nil)
56-
case Pattern(sym, _, stargs) => (sym, stargs.split(",").map(_.trim).toList)
57-
case _ => fail()
58-
}
49+
val result =
50+
AppliedType.parse(stpe) match {
51+
case (AppliedType(typename, appliedTypeArgs), _) =>
52+
val sym =
53+
if (typename.endsWith(".type")) mirror.staticModule(typename.stripSuffix(".type")).moduleClass
54+
else mirror.staticClass(typename)
55+
val tycon = sym.asType.toTypeConstructor
56+
appliedType(tycon, appliedTypeArgs.map(starg => typeFromString(mirror, starg.toString)))
57+
case _ =>
58+
sys.error(s"fatal: cannot unpickle $stpe")
5959
}
60-
61-
val sym = if (ssym.endsWith(".type")) mirror.staticModule(ssym.stripSuffix(".type")).moduleClass else mirror.staticClass(ssym)
62-
val tycon = sym.asType.toTypeConstructor
63-
appliedType(tycon, stargs.map(starg => typeFromString(mirror, starg)))
64-
}
6560
typeFromStringCache(stpe) = result
6661
result
6762
}

core/src/test/scala/pickling/pickling-spec.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,18 @@ object PicklingJsonSpec extends Properties("pickling-json") {
253253
readArr.sameElements(ia)
254254
})
255255

256+
property("Array[(Int, Double)]") = forAll((ia: Array[(Int, Double)]) => {
257+
val pickle: JSONPickle = ia.pickle
258+
val readArr = pickle.unpickle[Array[(Int, Double)]]
259+
readArr.sameElements(ia)
260+
})
261+
262+
property("Array[(String, Int)]") = forAll((ia: Array[(String, Int)]) => {
263+
val pickle: JSONPickle = ia.pickle
264+
val readArr = pickle.unpickle[Array[(String, Int)]]
265+
readArr.sameElements(ia)
266+
})
267+
256268
property("BigDecimal") = Prop forAll { (x: Double) =>
257269
val bd = new BigDecimal(x)
258270
val pickle: JSONPickle = bd.pickle
@@ -477,6 +489,18 @@ object PicklingBinarySpec extends Properties("pickling-binary") {
477489
readArr.sameElements(ia)
478490
})
479491

492+
property("Array[(Int, Double)]") = forAll((ia: Array[(Int, Double)]) => {
493+
val pickle: BinaryPickle = ia.pickle
494+
val readArr = pickle.unpickle[Array[(Int, Double)]]
495+
readArr.sameElements(ia)
496+
})
497+
498+
property("Array[(String, Int)]") = forAll((ia: Array[(String, Int)]) => {
499+
val pickle: BinaryPickle = ia.pickle
500+
val readArr = pickle.unpickle[Array[(String, Int)]]
501+
readArr.sameElements(ia)
502+
})
503+
480504
property("BigDecimal") = Prop forAll { (x: Double) =>
481505
val bd = new BigDecimal(x)
482506
val pickle: BinaryPickle = bd.pickle

0 commit comments

Comments
 (0)