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

[backport] SI-9375 add synthetic readResolve only for static modules #4757

Merged
merged 1 commit into from
Sep 22, 2015
Merged
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
[backport] SI-9375 add synthetic readResolve only for static modules
For inner modules, the synthetic readResolve method would cause the
module constructor to be invoked on de-serialization in certain
situations. See the discussion in the ticket.

Adds a comprehensive test around serializing and de-serializing
modules.
  • Loading branch information
lrytz committed Sep 22, 2015
commit 9a47e8124aaa2a94737ea3f01ef2fa1e95f91c49
16 changes: 14 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1182,11 +1182,23 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks {
val ModuleDef(_, _, impl) = moduleDef
val module = moduleDef.symbol
val moduleClass = module.moduleClass
val site = module.owner
val moduleName = module.name.toTermName
// The typer doesn't take kindly to seeing this ClassDef; we have to
// set NoType so it will be ignored.
val cdef = ClassDef(module.moduleClass, impl) setType NoType
val cdef = ClassDef(moduleClass, impl) setType NoType

// This code is related to the fix of SI-9375, which stops adding `readResolve` methods to
// non-static (nested) modules. Before the fix, the method would cause the module accessor
// to become notPrivate. To prevent binary changes in the 2.11.x branch, we mimic that behavior.
// There is a bit of code duplication between here and SyntheticMethods. We cannot call
// makeNotPrivate already in SyntheticMethod: that is during type checking, and not all references
// are resolved yet, so we cannot rename a definition. This code doesn't exist in the 2.12.x branch.
def hasConcreteImpl(name: Name) = moduleClass.info.member(name).alternatives exists (m => !m.isDeferred)
val hadReadResolveBeforeSI9375 = moduleClass.isSerializable && !hasConcreteImpl(nme.readResolve)
if (hadReadResolveBeforeSI9375)
moduleClass.sourceModule.makeNotPrivate(moduleClass.sourceModule.owner)

// Create the module var unless the immediate owner is a class and
// the module var already exists there. See SI-5012, SI-6712.
Expand All @@ -1210,7 +1222,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
def matchingInnerObject() = {
val newFlags = (module.flags | STABLE) & ~MODULE
val newInfo = NullaryMethodType(module.moduleClass.tpe)
val newInfo = NullaryMethodType(moduleClass.tpe)
val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo

DefDef(accessor, Select(This(site), module)) :: Nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ trait SyntheticMethods extends ast.TreeDSL {
clazz.isModuleClass
&& clazz.isSerializable
&& !hasConcreteImpl(nme.readResolve)
&& clazz.isStatic
)

def synthesize(): List[Tree] = {
Expand Down
4 changes: 0 additions & 4 deletions test/files/neg/t6666d.check

This file was deleted.

File renamed without changes.
3 changes: 1 addition & 2 deletions test/files/run/idempotency-case-classes.check
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ C(2,3)
case <synthetic> def unapply(x$0: C): Option[(Int, Int)] = if (x$0.==(null))
scala.this.None
else
Some.apply[(Int, Int)](scala.Tuple2.apply[Int, Int](x$0.x, x$0.y));
<synthetic> private def readResolve(): Object = C
Some.apply[(Int, Int)](scala.Tuple2.apply[Int, Int](x$0.x, x$0.y))
};
Predef.println(C.apply(2, 3))
}
Expand Down
1 change: 0 additions & 1 deletion test/files/run/repl-serialization.check
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ u: U = U
evaluating O
constructing A
== reconstituting into a fresh classloader
evaluating O
== evaluating reconstituted lambda
constructing A
60 changes: 60 additions & 0 deletions test/files/run/t9375.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
konstruktor: class A
konstruktor: class A$O$12$
konstruktor: class A$$anon$1
konstruktor: class A$A
konstruktor: class A$C
konstruktor: class C
konstruktor: class T$O$15$
konstruktor: class T$$anon$2
konstruktor: class T$A
konstruktor: class T$C
konstruktor: class A$N$
konstruktor: class T$N$
serializing outer objects should not initialize any nested objects
now initializing nested objects
konstruktor: class A$O$
konstruktor: class A$Op$
konstruktor: class A$N$O$
konstruktor: class A$N$Op$
konstruktor: class A$A$O$
konstruktor: class A$A$Op$
konstruktor: class A$T$O$
konstruktor: class A$T$Op$
konstruktor: class A$O$11$
konstruktor: class A$$anonfun$1$O$13$
konstruktor: class A$$anon$1$O$
konstruktor: class A$$anon$1$Op$
konstruktor: class T$O$
konstruktor: class T$Op$
konstruktor: class T$N$O$
konstruktor: class T$N$Op$
konstruktor: class T$A$O$
konstruktor: class T$A$Op$
konstruktor: class T$T$O$
konstruktor: class T$T$Op$
konstruktor: class T$O$14$
konstruktor: class T$$anonfun$2$O$16$
konstruktor: class T$$anon$2$O$
konstruktor: class T$$anon$2$Op$
no object konstruktors called when serializing / deserializing objects (starting at the outer or the object itself)
deserializing outer objects with non-initialized inners again
accessing modules triggers initialization
konstruktor: class A$O$
konstruktor: class A$Op$
konstruktor: class A$N$O$
konstruktor: class A$N$Op$
deserializing creates a new object graph, including new scala 'object' instances, no matter where serialization starts
init static module M and field v
konstruktor: class M$
konstruktor: class M$O$18$
serDeser does not initialize nested static modules
init M.O
konstruktor: class M$O$
serDeser nested static module
objects declared in field decls are not static modules, so they deserialize to new instances
init lazy val M.w
objects declared in lazy val are not static modules either
konstruktor: class M$O$19$
object declared in a function: new instance created on each invocation
konstruktor: class M$$anonfun$3$O$20$
konstruktor: class M$$anonfun$3$O$20$
Loading