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

Commit c273eee

Browse files
committed
Adds ability to pickle/unpickle things marked @transient
1 parent 6e92d67 commit c273eee

File tree

3 files changed

+73
-6
lines changed

3 files changed

+73
-6
lines changed

core/src/main/scala/pickling/Macros.scala

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,26 @@ trait UnpicklerMacros extends Macro {
258258

259259
val someConstructorIsPrivate = ctors.exists(_.isPrivate)
260260
// println(s"someConstructorIsPrivate: $someConstructorIsPrivate")
261-
val canCallCtor = !someConstructorIsPrivate && !cir.fields.exists(_.isErasedParam) && isPreciseType
262-
// println(s"canCallCtor: $canCallCtor")
261+
val canCallCtor = !someConstructorIsPrivate && !cir.fields.exists(_.isErasedParam) && isPreciseType && {
262+
// there must not be a transient ctor param
263+
// STEP 1: we need to figure out if there is a transient ctor param
264+
val primaryCtor = tpe.declaration(nme.CONSTRUCTOR) match {
265+
case overloaded: TermSymbol => overloaded.alternatives.head.asMethod // NOTE: primary ctor is always the first in the list
266+
case primaryCtor: MethodSymbol => primaryCtor
267+
case NoSymbol => NoSymbol
268+
}
269+
270+
val allAccessors = tpe.declarations.collect { case meth: MethodSymbol if meth.isAccessor || meth.isParamAccessor => meth }
271+
272+
val (filteredAccessors, transientAccessors) = allAccessors.partition(irs.notMarkedTransient) // shoulsai iaobjsobj aaaobjsecta insteeyd m d daybe
273+
274+
val hasTransientParam = (primaryCtor != NoSymbol) && primaryCtor.asMethod.paramss.flatten.exists { sym =>
275+
transientAccessors.exists(acc => acc.name.toString == sym.name.toString)
276+
}
277+
278+
!hasTransientParam
279+
}
280+
// STEP 2: remove transient fields from the "pending fields", the fields that need to be restored.
263281

264282
// TODO: for ultimate loop safety, pendingFields should be hoisted to the outermost unpickling scope
265283
// For example, in the snippet below, when unpickling C, we'll need to move the b.c assignment not

core/src/main/scala/pickling/ir/IRs.scala

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,43 @@ class IRs[U <: Universe with Singleton](val uni: U) {
3333
private type C = ClassIR
3434

3535
// TODO: minimal versus verbose PickleFormat. i.e. someone might want all concrete inherited fields in their pickle
36+
37+
def notMarkedTransient(sym: TermSymbol): Boolean = {
38+
//println(s"checking annots of ${sym.toString}...")
39+
val tr = scala.util.Try {
40+
if (sym.accessed != NoSymbol) {
41+
val overall = sym.accessed.annotations.forall { a =>
42+
val res = (a.tpe =:= typeOf[scala.transient])
43+
!res
44+
}
45+
overall
46+
} else true // if there is no backing field, then it cannot be marked transient
47+
}
48+
if (tr.isFailure) {
49+
}
50+
tr.isFailure || tr.get
51+
}
52+
53+
/** Creates FieldIRs for the given type, tp.
54+
*/
3655
private def fields(tp: Type): Q = {
3756
val ctor = tp.declaration(nme.CONSTRUCTOR) match {
3857
case overloaded: TermSymbol => overloaded.alternatives.head.asMethod // NOTE: primary ctor is always the first in the list
3958
case primaryCtor: MethodSymbol => primaryCtor
4059
case NoSymbol => NoSymbol
4160
}
42-
val ctorParams = if (ctor != NoSymbol) ctor.asMethod.paramss.flatten.map(_.asTerm) else Nil
43-
val allAccessors = tp.declarations.collect{ case meth: MethodSymbol if meth.isAccessor || meth.isParamAccessor => meth }
61+
62+
val allAccessors = tp.declarations.collect { case meth: MethodSymbol if meth.isAccessor || meth.isParamAccessor => meth }
63+
64+
val (filteredAccessors, transientAccessors) = allAccessors.partition(notMarkedTransient)
65+
66+
val ctorParams = if (ctor != NoSymbol) ctor.asMethod.paramss.flatten.flatMap { sym =>
67+
if (transientAccessors.exists(acc => acc.name.toString == sym.name.toString)) {
68+
//println(s"found a BAD accessor: $sym")
69+
List()
70+
} else List(sym.asTerm)
71+
} else Nil
72+
4473
val (paramAccessors, otherAccessors) = allAccessors.partition(_.isParamAccessor)
4574

4675
def mkFieldIR(sym: TermSymbol, param: Option[TermSymbol], accessor: Option[MethodSymbol]) = {
@@ -54,15 +83,16 @@ class IRs[U <: Universe with Singleton](val uni: U) {
5483
val varGetters = otherAccessors.collect{ case meth if meth.isGetter && meth.accessed != NoSymbol && meth.accessed.asTerm.isVar => meth }
5584
val varFields = varGetters.map(sym => mkFieldIR(sym, None, Some(sym)))
5685

57-
paramFields ++ varFields
86+
val res = paramFields ++ varFields
87+
res
5888
}
5989

6090
private def composition(f1: (Q, Q) => Q, f2: (C, C) => C, f3: C => List[C]) =
6191
(c: C) => f3(c).reverse.reduce[C](f2)
6292

6393
private val f1 = (q1: Q, q2: Q) => q1 ++ q2
6494

65-
private val f2 = (c1: C, c2: C) => ClassIR(c2.tpe, c1, fields(c2.tpe))
95+
private val f2 = (c1: C, c2: C) => ClassIR(c2.tpe, c1, /*fields(c2.tpe)*/c2.fields) // here: fields is called a 2nd time.
6696

6797
private val f3 = (c: C) =>
6898
c.tpe.baseClasses
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package scala.pickling.transienttest
2+
3+
import org.scalatest.FunSuite
4+
import scala.pickling._
5+
import json._
6+
7+
case class Person(val name: String , @transient val ssNumber: Int) {
8+
override def toString = s"Person($name)"
9+
}
10+
11+
class TransientSimpleTest extends FunSuite {
12+
test("main") {
13+
val per = Person("Jenny", 123)
14+
val p: JSONPickle = per.pickle
15+
val up = p.unpickle[Person]
16+
assert(up.ssNumber == 0)
17+
assert(per.toString == up.toString)
18+
}
19+
}

0 commit comments

Comments
 (0)