diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2015-05-12 15:28:35 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2015-05-17 19:30:10 +1000 |
commit | afa2ff9f76123ab982dc5bb2f1110bb58e75c68c (patch) | |
tree | 90f720ed31e91d64beaa3031de7394fd545c27ea /src/reflect | |
parent | 6ad9b44b27ede70ec723204bd80361d60f448c1a (diff) | |
download | scala-afa2ff9f76123ab982dc5bb2f1110bb58e75c68c.tar.gz scala-afa2ff9f76123ab982dc5bb2f1110bb58e75c68c.tar.bz2 scala-afa2ff9f76123ab982dc5bb2f1110bb58e75c68c.zip |
[indylambda] Support lambda {de}serialization
To support serialization, we use the alternative lambda metafactory
that lets us specify that our anonymous functions should extend the
marker interface `scala.Serializable`. They will also have a
`writeObject` method added that implements the serialization proxy
pattern using `j.l.invoke.SerializedLamba`.
To support deserialization, we synthesize a `$deserializeLamba$`
method in each class with lambdas. This will be called reflectively by
`SerializedLambda#readResolve`. This method in turn delegates to
`LambdaDeserializer`, currently defined [1] in `scala-java8-compat`,
that uses `LambdaMetafactory` to spin up the anonymous class and
instantiate it with the deserialized environment.
Note: `LambdaDeserializer` can reuses the anonymous class on subsequent
deserializations of a given lambda, in the same spirit as an
invokedynamic call site only spins up the class on the first time
it is run. But first we'll need to host a cache in a static field
of each lambda hosting class. This is noted as a TODO and a failing
test, and will be updated in the next commit.
`LambdaDeserializer` will be moved into our standard library in
the 2.12.x branch, where we can introduce dependencies on the
Java 8 standard library.
The enclosed test cases must be manually run with indylambda enabled.
Once we enable indylambda by default on 2.12.x, the test will
actually test the new feature.
```
% echo $INDYLAMBDA
-Ydelambdafy:method -Ybackend:GenBCode -target:jvm-1.8 -classpath .:scala-java8-compat_2.11-0.5.0-SNAPSHOT.jar
% qscala $INDYLAMBDA -e "println((() => 42).getClass)"
class Main$$anon$1$$Lambda$1/1183231938
% qscala $INDYLAMBDA -e "assert(classOf[scala.Serializable].isInstance(() => 42))"
% qscalac $INDYLAMBDA test/files/run/lambda-serialization.scala && qscala $INDYLAMBDA Test
```
This commit contains a few minor refactorings to the code that
generates the invokedynamic instruction to use more meaningful
names and to reuse Java signature generation code in ASM rather
than the DIY approach.
[1] https://github.com/scala/scala-java8-compat/pull/37
Diffstat (limited to 'src/reflect')
4 files changed, 5 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 73ffb267a9..806fc37617 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -514,6 +514,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature] lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature] + lazy val LambdaMetaFactory = getClassIfDefined("java.lang.invoke.LambdaMetafactory") lazy val MethodHandle = getClassIfDefined("java.lang.invoke.MethodHandle") // Option classes diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c0562b0679..63e2ca0dbe 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -1167,6 +1167,8 @@ trait StdNames { final val Invoke: TermName = newTermName("invoke") final val InvokeExact: TermName = newTermName("invokeExact") + final val AltMetafactory: TermName = newTermName("altMetafactory") + val Boxed = immutable.Map[TypeName, TypeName]( tpnme.Boolean -> BoxedBoolean, tpnme.Byte -> BoxedByte, diff --git a/src/reflect/scala/reflect/internal/transform/PostErasure.scala b/src/reflect/scala/reflect/internal/transform/PostErasure.scala index 466c6133b2..dd4f044818 100644 --- a/src/reflect/scala/reflect/internal/transform/PostErasure.scala +++ b/src/reflect/scala/reflect/internal/transform/PostErasure.scala @@ -9,8 +9,7 @@ trait PostErasure { object elimErasedValueType extends TypeMap { def apply(tp: Type) = tp match { case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp))) - case ErasedValueType(_, underlying) => - underlying + case ErasedValueType(_, underlying) => underlying case _ => mapOver(tp) } } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 1c0aa7cf6d..8c03ee7ca3 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -310,6 +310,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.QuasiquoteClass_api_unapply definitions.ScalaSignatureAnnotation definitions.ScalaLongSignatureAnnotation + definitions.LambdaMetaFactory definitions.MethodHandle definitions.OptionClass definitions.OptionModule |