Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial picklers for nullary spores #24

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */

package scala.spores

import scala.reflect.macros.blackbox.Context

import scala.pickling._

trait NullarySporePicklerImpl {

def genNullarySporePicklerImpl[R: c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._

val rtpe = weakTypeOf[R]
val picklerName = c.freshName(TermName("NullarySporePickler"))

q"""
object $picklerName extends scala.pickling.Pickler[scala.spores.NullarySpore[$rtpe]] {
def tag =
implicitly[scala.pickling.FastTypeTag[scala.spores.NullarySpore[$rtpe]]]

def pickle(picklee: scala.spores.NullarySpore[$rtpe], builder: scala.pickling.PBuilder): Unit = {
builder.beginEntry(picklee)

builder.putField("className", b => {
b.hintTag(scala.pickling.FastTypeTag.String)
b.hintStaticallyElidedType()
scala.pickling.pickler.AllPicklers.stringPickler.pickle(picklee.className, b)
})

builder.endEntry()
}
}
$picklerName
"""
}

def genNullarySporeCSPicklerImpl[R: c.WeakTypeTag, U: c.WeakTypeTag](c: Context)(cPickler: c.Tree): c.Tree = {
import c.universe._

val rtpe = weakTypeOf[R]
val utpe = weakTypeOf[U]

def isEffectivelyPrimitive(tpe: c.Type): Boolean = tpe match {
case TypeRef(_, sym: ClassSymbol, _) if sym.isPrimitive => true
case TypeRef(_, sym, eltpe :: Nil) if sym == definitions.ArrayClass && isEffectivelyPrimitive(eltpe) => true
case _ => false
}

val sporeTypeName = TypeName("NullarySporeWithEnv")
val picklerName = c.freshName(TermName("NullarySporePickler"))

q"""
val capturedPickler = $cPickler
object $picklerName extends scala.pickling.Pickler[scala.spores.NullarySporeWithEnv[$rtpe] { type Captured = $utpe }] {
def tag =
implicitly[scala.pickling.FastTypeTag[scala.spores.NullarySporeWithEnv[$rtpe] { type Captured = $utpe }]]

def pickle(picklee: scala.spores.NullarySporeWithEnv[$rtpe] { type Captured = $utpe }, builder: scala.pickling.PBuilder): Unit = {
// println("[genNullarySporeCSPicklerImpl]")
builder.beginEntry(picklee)

builder.putField("className", b => {
b.hintTag(scala.pickling.FastTypeTag.String)
b.hintStaticallyElidedType()
scala.pickling.pickler.AllPicklers.stringPickler.pickle(picklee.className, b)
})

builder.putField("captured", b => {
b.hintTag(capturedPickler.tag)
${if (isEffectivelyPrimitive(utpe)) q"b.hintStaticallyElidedType()" else q""}
capturedPickler.pickle(picklee.asInstanceOf[$sporeTypeName[$rtpe]].captured.asInstanceOf[$utpe], b)
})

builder.endEntry()
}
}
$picklerName
"""
}

def genNullarySporeCSUnpicklerImpl[R: c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._

val rtpe = weakTypeOf[R]
val unpicklerName = c.freshName(TermName("NullarySporeUnpickler"))
val utils = new PicklerUtils[c.type](c)
val reader = TermName("reader")
val readClassName = utils.readClassNameTree(reader)

q"""
object $unpicklerName extends scala.pickling.Unpickler[scala.spores.NullarySpore[$rtpe]] {
def tag =
implicitly[scala.pickling.FastTypeTag[scala.spores.NullarySpore[$rtpe]]]

def unpickle(tag: String, $reader: scala.pickling.PReader): Any = {
val result = $readClassName

// println("[genNullarySporeCSUnpicklerImpl] creating instance of class " + result)
val clazz = java.lang.Class.forName(result)
val sporeInst = (try clazz.newInstance() catch {
case t: Throwable =>
val inst = scala.concurrent.util.Unsafe.instance.allocateInstance(clazz)
val privateClassNameField = clazz.getDeclaredField("_className")
privateClassNameField.setAccessible(true)
privateClassNameField.set(inst, result)
inst
}).asInstanceOf[scala.spores.NullarySpore[$rtpe]]

if (sporeInst.isInstanceOf[scala.spores.NullarySporeWithEnv[$rtpe]]) {
// println("[genNullarySporeCSUnpicklerImpl] spore class is NullarySporeWithEnv")
val sporeWithEnvInst = sporeInst.asInstanceOf[scala.spores.NullarySporeWithEnv[$rtpe]]
val reader3 = $reader.readField("captured")
val tag3 = reader3.beginEntry()
val value = {
if (reader3.atPrimitive) {
reader3.readPrimitive()
} else {
val unpickler3 = scala.pickling.runtime.RuntimeUnpicklerLookup.genUnpickler(scala.reflect.runtime.currentMirror, tag3)
unpickler3.unpickle(tag3, reader3)
}
}
reader3.endEntry()
sporeWithEnvInst.captured = value.asInstanceOf[sporeWithEnvInst.Captured]
}

sporeInst
}
}
$unpicklerName
"""
}

}
11 changes: 10 additions & 1 deletion spores-pickling/src/main/scala/scala/spores/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import scala.reflect.macros.blackbox.Context
import scala.pickling._


object SporePickler extends SimpleSporePicklerImpl {
object SporePickler extends SimpleSporePicklerImpl with NullarySporePicklerImpl {
/*implicit*/
def genSporePickler[T, R, U](implicit cPickler: Pickler[U], cUnpickler: Unpickler[U])
: Pickler[Spore[T, R] { type Captured = U }] with Unpickler[Spore[T, R] { type Captured = U }] = macro genSporePicklerImpl[T, R, U]
Expand Down Expand Up @@ -118,6 +118,12 @@ object SporePickler extends SimpleSporePicklerImpl {
implicit def genSimpleSpore3Pickler[T1, T2, T3, R]: Pickler[Spore3[T1, T2, T3, R]] =
macro genSimpleSpore3PicklerImpl[T1, T2, T3, R]

implicit def genNullarySporePickler[R]: Pickler[NullarySpore[R]] =
macro genNullarySporePicklerImpl[R]

implicit def genNullarySporeCSPickler[R, U](implicit cPickler: Pickler[U]): Pickler[NullarySporeWithEnv[R] { type Captured = U }] =
macro genNullarySporeCSPicklerImpl[R, U]

// type `U` </: `Product`
implicit def genSporeCSPickler[T, R, U](implicit cPickler: Pickler[U]): Pickler[SporeWithEnv[T, R] { type Captured = U }] =
macro genSporeCSPicklerImpl[T, R, U]
Expand Down Expand Up @@ -384,6 +390,9 @@ object SporePickler extends SimpleSporePicklerImpl {
}


implicit def genNullarySporeCSUnpickler[R]: Unpickler[NullarySpore[R]] =
macro genNullarySporeCSUnpicklerImpl[R]

implicit def genSporeCSUnpickler[T, R]: Unpickler[Spore[T, R]/* { type Captured }*/] =
macro genSporeCSUnpicklerImpl[T, R]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,51 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

import scala.concurrent.Future

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

import SporePickler._


class LocalSilo[U, T <: Traversable[U]](val value: T) {
def send(): Future[T] =
Future.successful(value)
}

case class InitSiloFun[U, T <: Traversable[U]](fun: NullarySpore[LocalSilo[U, T]], refId: Int)


object GlobalFuns {
private val summary = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum."""

private lazy val words = summary.replace('\n', ' ').split(" ")

def randomWord(random: scala.util.Random): String = {
val index = random.nextInt(words.length)
words(index)
}

def populateSilo(numLines: Int, random: scala.util.Random): LocalSilo[String, List[String]] = {
// each string is a concatenation of 10 random words, separated by space
val buffer = collection.mutable.ListBuffer[String]()
val lines = for (i <- 0 until numLines) yield {
val tenWords = for (_ <- 1 to 10) yield randomWord(random)
buffer += tenWords.mkString(" ")
}
new LocalSilo(buffer.toList)
}
}


@RunWith(classOf[JUnit4])
class PicklingSpec {
@Test
Expand Down Expand Up @@ -158,6 +197,30 @@ class PicklingSpec {
assert(res2 == "arg1: 5, arg2: hi, arg3: -")
}

@Test
def `simple pickling of nullary spore`(): Unit = {
val s = spore { delayed { List(2, 3, 4) } }
val res = s.pickle
val up = res.unpickle[NullarySpore[List[Int]]]
val res2 = up()
assert(res2.toString == "List(2, 3, 4)")
}

@Test
def `simple pickling of nullary spore with one captured variable`(): Unit = {
val x = 1
val s = spore {
val localX = x
delayed {
List(1, 2, 3).map(_ + localX)
}
}
val res = s.pickle
val up = res.unpickle[NullarySpore[List[Int]]]
val res2 = up()
assert(res2.toString == "List(2, 3, 4)")
}

def doPickle[T <: Spore[Int, String]: Pickler: Unpickler](spor: T) = {
val unpickler = implicitly[Unpickler[T]]
val res = spor.pickle
Expand All @@ -178,4 +241,19 @@ class PicklingSpec {
}
doPickle(s)
}

@Test
def `pickle case class with nullary spore`(): Unit = {
val s: NullarySpore[LocalSilo[String, List[String]]] = spore {
delayed {
GlobalFuns.populateSilo(10, new scala.util.Random(100))
}
}

val init = InitSiloFun(s, 5)
val p = init.pickle
val up = p.unpickle[InitSiloFun[String, List[String]]]
assert(up.refId == 5)
assert(up.fun().value.size == 10)
}
}