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

Commit

Permalink
Handle sealed classes in check for non-cyclic types
Browse files Browse the repository at this point in the history
When deciding to delay the initialization of fields due to
possible cycles, sealed classes were not handled. This could
cause issues, such as #128, where the initialization of
a constructor parameter was delayed and thus the parameter
was null during the execution of the constructor.
  • Loading branch information
phaller committed May 20, 2014
1 parent c64d5d6 commit 95501ff
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
41 changes: 34 additions & 7 deletions core/src/main/scala/pickling/Tools.scala
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,40 @@ abstract class ShareAnalyzer[U <: Universe](val u: U) {
if (visited(currTpe)) {
if (tpe <:< currTpe) true // TODO: make sure this sanely works for polymorphic types
else loop(rest, visited)
} else if (currTpe.isNotNullable || currTpe.isEffectivelyPrimitive || currSym == StringClass || currSym.isModuleClass) loop(rest, visited)
// TODO: extend the traversal logic to support sealed classes
// when doing that don't forget:
// 1) sealeds can themselves be extended, so we need to recur
// 2) the entire sealed hierarchy should be added to todo
else if (!currSym.isFinal) true // NOTE: returning true here is important for soundness!
else {
} else if (currTpe.isNotNullable || currTpe.isEffectivelyPrimitive || currSym == StringClass || currSym.isModuleClass) {
loop(rest, visited)
} else if (currSym.isClass && currSym.asClass.isSealed) {
val currTargs: List[Type] =
currTpe.asInstanceOf[scala.reflect.internal.SymbolTable#Type]
.dealias
.typeArgs
.map(_.asInstanceOf[Type])

// field types have to be OK
val more = flattenedClassIR(currTpe).fields.map(_.tpe)

// all known subclasses have to be OK, too
val ksc = currSym.asClass.knownDirectSubclasses
val subclasses = ksc.map(sym => sym.asClass.toTypeIn(currTpe))

// collect field types of all subclasses
val fieldTypes = subclasses.flatMap { subclassTpe =>
flattenedClassIR(subclassTpe).fields.map(_.tpe)
}

// for field types that are type parameters,
// use type arguments of currTpe (currTargs) instead
val fieldTypesToCheck = fieldTypes.flatMap { tp =>
if (tp.typeSymbol.isParameter) currTargs
else List(tp)
}

val allTodo = rest ++ more ++ fieldTypesToCheck
loop(allTodo, visited + currTpe)
}
else if (!currSym.isFinal) {
true // NOTE: returning true here is important for soundness!
} else {
val more = flattenedClassIR(currTpe).fields.map(_.tpe)
loop(rest ++ more, visited + currTpe)
}
Expand Down
22 changes: 22 additions & 0 deletions core/src/test/scala/pickling/issue128.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package scala.pickling.test.issue128

import scala.pickling._
import scala.pickling.json._

import org.scalatest.FunSuite

case class A(intOpt: Option[Int]) {
intOpt match {
case Some(int) =>
case None =>
}
}

class Issue128Test extends FunSuite {
test("Issue #128") {
val opt = Some(5)
val a = A(opt)
val pickle = a.pickle
assert(pickle.unpickle[A] === a)
}
}

0 comments on commit 95501ff

Please sign in to comment.