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

Commit 4637393

Browse files
committed
Progress on dealing with ctors
1 parent e54885d commit 4637393

File tree

5 files changed

+155
-26
lines changed

5 files changed

+155
-26
lines changed

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

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,29 +108,37 @@ trait PicklerMacros extends Macro {
108108
)
109109
""")
110110
} else (nonLoopyFields ++ loopyFields).flatMap(fir => {
111+
// for each field, compute a tree for pickling it
112+
// (or empty list, if impossible)
113+
114+
def putField(getterLogic: Tree) = {
115+
def wrap(pickleLogic: Tree) = q"builder.putField(${fir.name}, b => $pickleLogic)"
116+
wrap {
117+
if (fir.tpe.typeSymbol.isEffectivelyFinal) q"""
118+
b.hintStaticallyElidedType()
119+
$getterLogic.pickleInto(b)
120+
""" else q"""
121+
val subPicklee: ${fir.tpe} = $getterLogic
122+
if (subPicklee == null || subPicklee.getClass == classOf[${fir.tpe}]) b.hintDynamicallyElidedType() else ()
123+
subPicklee.pickleInto(b)
124+
"""
125+
}
126+
}
127+
111128
if (sym.isModuleClass) {
112129
Nil
113130
} else if (fir.hasGetter) {
114-
def putField(getterLogic: Tree) = {
115-
def wrap(pickleLogic: Tree) = q"builder.putField(${fir.name}, b => $pickleLogic)"
116-
wrap {
117-
if (fir.tpe.typeSymbol.isEffectivelyFinal) q"""
118-
b.hintStaticallyElidedType()
119-
$getterLogic.pickleInto(b)
120-
""" else q"""
121-
val subPicklee: ${fir.tpe} = $getterLogic
122-
if (subPicklee == null || subPicklee.getClass == classOf[${fir.tpe}]) b.hintDynamicallyElidedType() else ()
123-
subPicklee.pickleInto(b)
124-
"""
125-
}
126-
}
131+
127132
if (fir.isPublic) List(putField(q"picklee.${newTermName(fir.name)}"))
128133
else reflectively("picklee", fir)(fm => putField(q"$fm.get.asInstanceOf[${fir.tpe}]"))
129134
} else {
130-
// NOTE: this means that we've encountered a primary constructor parameter elided in the "constructors" phase
131-
// we can do nothing about that, so we don't serialize this field right now leaving everything to the unpickler
132-
// when deserializing we'll have to use the Unsafe.allocateInstance strategy
133-
Nil
135+
//val clazz = universe.mirror.runtimeClass(tpe.erasure)
136+
//if (Try(clazz.getDeclaredField(fir.name)).isSuccess) {
137+
// pickle field using reflection
138+
reflectivelyWithoutGetter("picklee", fir)(fvalue => putField(q"$fvalue.asInstanceOf[${fir.tpe}]"))
139+
/*} else {
140+
Nil
141+
}*/
134142
}
135143
})
136144
val endEntry = q"builder.endEntry()"

core/src/main/scala/pickling/Runtime.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,11 @@ class InterpretedUnpicklerRuntime(mirror: Mirror, tag: FastTypeTag[_])(implicit
189189
List[FieldIR]()
190190
} else {
191191
val (nonLoopyFields, loopyFields) = cir.fields.partition(fir => !shouldBotherAboutLooping(fir.tpe))
192-
(nonLoopyFields ++ loopyFields).filter(fir => fir.isNonParam || fir.isReifiedParam)
192+
(nonLoopyFields ++ loopyFields).filter(fir =>
193+
fir.hasGetter || {
194+
// exists as Java field
195+
scala.util.Try(clazz.getDeclaredField(fir.name)).isSuccess
196+
})
193197
}
194198

195199
def fieldVals = pendingFields.map(fir => {
@@ -223,10 +227,19 @@ class InterpretedUnpicklerRuntime(mirror: Mirror, tag: FastTypeTag[_])(implicit
223227
if (shouldBotherAboutSharing(tpe)) registerUnpicklee(inst, preregisterUnpicklee())
224228
val im = mirror.reflect(inst)
225229

230+
debug(s"pendingFields: ${pendingFields.size}")
231+
debug(s"fieldVals: ${fieldVals.size}")
232+
226233
pendingFields.zip(fieldVals) foreach {
227234
case (fir, fval) =>
228-
val fmX = im.reflectField(fir.field.get)
229-
fmX.set(fval)
235+
if (fir.hasGetter) {
236+
val fmX = im.reflectField(fir.field.get)
237+
fmX.set(fval)
238+
} else {
239+
val javaField = clazz.getDeclaredField(fir.name)
240+
javaField.setAccessible(true)
241+
javaField.set(inst, fval)
242+
}
230243
}
231244

232245
inst

core/src/main/scala/pickling/RuntimePickler.scala

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.pickling
22

3+
import java.lang.reflect.Field
4+
35
import scala.reflect.runtime.{universe => ru}
46
import ir._
57

@@ -37,7 +39,7 @@ class RuntimePickler(classLoader: ClassLoader, clazz: Class[_])(implicit pf: Pic
3739
import ru._
3840

3941
sealed abstract class Logic(fir: irs.FieldIR, isEffFinal: Boolean) {
40-
def run(builder: PBuilder, im: ru.InstanceMirror): Unit = {
42+
def run(builder: PBuilder, picklee: Any, im: ru.InstanceMirror): Unit = {
4143
val fldMirror = im.reflectField(fir.field.get)
4244
val fldValue: Any = fldMirror.get
4345
val fldClass = if (fldValue != null) fldValue.getClass else null
@@ -83,6 +85,41 @@ class RuntimePickler(classLoader: ClassLoader, clazz: Class[_])(implicit pf: Pic
8385
}
8486
}
8587

88+
sealed class PrivateJavaFieldLogic(fir: irs.FieldIR, field: Field) extends Logic(fir, false) {
89+
override def run(builder: PBuilder, picklee: Any, im: ru.InstanceMirror): Unit = {
90+
field.setAccessible(true)
91+
val fldValue = field.get(picklee)
92+
val fldClass = if (fldValue != null) fldValue.getClass else null
93+
94+
//debug(s"pickling field of type: ${fir.tpe.toString}")
95+
//debug(s"isEffFinal: $isEffFinal")
96+
//debug(s"field value: $fldValue")
97+
//debug(s"field class: ${fldClass.getName}")
98+
99+
// idea: picklers for all fields could be created and cached once and for all.
100+
// however, it depends on whether the type of the field is effectively final or not.
101+
// essentially, we have to emulate the behavior of generated picklers, which make
102+
// the same decision.
103+
val fldPickler = SPickler.genPickler(classLoader, fldClass).asInstanceOf[SPickler[Any]]
104+
//debug(s"looked up field pickler: $fldPickler")
105+
106+
builder.putField(field.getName, b => {
107+
pickleLogic(fldClass, fldValue, b, fldPickler)
108+
})
109+
}
110+
111+
def pickleLogic(fldClass: Class[_], fldValue: Any, b: PBuilder, fldPickler: SPickler[Any]): Unit = {
112+
pickleInto(fldClass, fldValue, b, fldPickler)
113+
}
114+
}
115+
116+
final class PrivateEffectivelyFinalJavaFieldLogic(fir: irs.FieldIR, field: Field) extends PrivateJavaFieldLogic(fir, field) {
117+
override def pickleLogic(fldClass: Class[_], fldValue: Any, b: PBuilder, fldPickler: SPickler[Any]): Unit = {
118+
b.hintStaticallyElidedType()
119+
pickleInto(fldClass, fldValue, b, fldPickler)
120+
}
121+
}
122+
86123
// difference to old runtime pickler: create tag based on fieldClass instead of fir.tpe
87124
def pickleInto(fieldClass: Class[_], fieldValue: Any, builder: PBuilder, pickler: SPickler[Any]): Unit = {
88125
val fieldTag = FastTypeTag.mkRaw(fieldClass, mirror)
@@ -95,20 +132,51 @@ class RuntimePickler(classLoader: ClassLoader, clazz: Class[_])(implicit pf: Pic
95132
new SPickler[Any] {
96133
val format: PickleFormat = pf
97134

98-
val fields: List[Logic] =
99-
cir.fields.filter(_.hasGetter).map { fir =>
135+
val fields: List[Logic] = cir.fields.flatMap { fir =>
136+
if (fir.hasGetter) {
137+
//debug(s"pickling field of type ${fir.tpe.key}")
138+
List(
139+
if (fir.tpe.typeSymbol.isEffectivelyFinal) new EffectivelyFinalLogic(fir)
140+
else if (fir.tpe.typeSymbol.asType.isAbstractType) new AbstractLogic(fir)
141+
else new DefaultLogic(fir)
142+
)
143+
} else
144+
try {
145+
val javaField = clazz.getDeclaredField(fir.name)
146+
List(
147+
if (fir.tpe.typeSymbol.isEffectivelyFinal) new PrivateEffectivelyFinalJavaFieldLogic(fir, javaField)
148+
else new PrivateJavaFieldLogic(fir, javaField)
149+
)
150+
} catch {
151+
case e: java.lang.NoSuchFieldException => List()
152+
}
153+
}
154+
/*
155+
{
156+
debug(s"cir.fields: ${cir.fields.mkString(",")}")
157+
158+
val regularFields = cir.fields.filter(_.hasGetter)
159+
debug(s"regular fields with getters: ${regularFields.mkString(",")}")
160+
161+
val ctorParamsWithoutGetter =
162+
clazz.getDeclaredFields().filter(df => regularFields.forall(_.name != df.getName))
163+
debug(s"ctor params without getters: ${ctorParamsWithoutGetter.mkString(",")}")
164+
165+
ctorParamsWithoutGetter.map(cp => new CtorParamLogic(cp)).toList ++ regularFields.map { fir =>
166+
debug(s"pickling field of type ${fir.tpe.key}")
100167
if (fir.tpe.typeSymbol.isEffectivelyFinal) new EffectivelyFinalLogic(fir)
101168
else if (fir.tpe.typeSymbol.asType.isAbstractType) new AbstractLogic(fir)
102169
else new DefaultLogic(fir)
103170
}
104-
171+
}
172+
*/
105173
def putFields(picklee: Any, builder: PBuilder): Unit = {
106174
val im = mirror.reflect(picklee)
107-
fields.foreach(_.run(builder, im))
175+
fields.foreach(_.run(builder, picklee, im))
108176
}
109177

110178
def pickle(picklee: Any, builder: PBuilder): Unit = {
111-
//debug(s"pickling object of type: ${tag.key}")
179+
debug(s"pickling object of type: ${tag.key}")
112180
builder.beginEntry(picklee)
113181
putFields(picklee, builder)
114182
builder.endEntry()

core/src/main/scala/pickling/Tools.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,17 @@ abstract class Macro { self =>
375375
private var reflectivePrologueEmitted = false // TODO: come up with something better
376376
def reflectively(target: String, fir: FieldIR)(body: Tree => Tree): List[Tree] = reflectively(newTermName(target), fir)(body)
377377

378+
def reflectivelyWithoutGetter(target: String, fir: FieldIR)(body: Tree => Tree): List[Tree] = {
379+
val pickleeName = newTermName(target)
380+
val getFieldValue = q"""
381+
val clazz = $pickleeName.getClass
382+
val javaField: java.lang.reflect.Field = clazz.getDeclaredField(${fir.name})
383+
javaField.setAccessible(true)
384+
javaField.get($pickleeName)
385+
"""
386+
List(body(getFieldValue))
387+
}
388+
378389
/**
379390
* requires: !fir.accessor.isEmpty
380391
*/
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package scala.pickling.ctorparams
2+
3+
import org.scalatest.FunSuite
4+
import scala.pickling._
5+
import json._
6+
7+
class Partitioner(numParts: Int) {
8+
def numPartitions = numParts
9+
override def toString = s"Partitioner($numParts)"
10+
}
11+
12+
class AllRuntimeCtorsParamTest extends FunSuite {
13+
test("main") {
14+
val par: Any = new Partitioner(8)
15+
val p: JSONPickle = par.pickle
16+
val up = p.unpickle[Any]
17+
assert(par.toString == up.toString)
18+
}
19+
}
20+
21+
class StaticTriggeredCtorsParamTest extends FunSuite {
22+
test("main") {
23+
// val par = new Partitioner(8)
24+
// val p: JSONPickle = par.pickle
25+
// val up = p.unpickle[Partitioner]
26+
// assert(par.toString == up.toString)
27+
assert(true)
28+
}
29+
}

0 commit comments

Comments
 (0)