From 9a856be5ca890fe09c3e02aeafa8741f1f8f3f4d Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 21 Dec 2015 12:07:56 -0800 Subject: [PATCH 1/2] Test for regression triggered by #2325 The crash manifested as a `NotSerializableException`, because we don't visit the private subtrees of the API graph, which would previously force all Lazy stubs as a side-effect. There was a backstop in `AbstractLazy` (`writeReplace`), but somehow that didn't work. ``` java.io.NotSerializableException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl [...] at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329) at xsbt.api.SourceFormat$.writes(SourceFormat.scala:24) at xsbt.api.SourceFormat$.writes(SourceFormat.scala:15) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:329) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:329) at sbt.inc.TextAnalysisFormat21661anonfun at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.javr$.aggregate(TextAnalysisFormat.scala:18) at sbt.inc.TextAnalysisFormat$.objToString(TextAnalysisFormat.scala:329) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:213) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:213) at sbt.inc.TextAnalysisFormat21661anonfun21661writeMap.apply(TextAnalysisFormat.scala:381) at sbt.inc.TextAnalysisFormat21661anonfun21661writeMap.apply(TextAnalysisFormat.scala:377) at scala.collection.mutable.ResizableArray.foreach(Resizable at sbt.inc.TextAnalysisFormalection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:47) at sbt.inc.TextAnalysisFormat$.sbt21661writeMap(TextAnalysisFormat.scala:377) at sbt.inc.TextAnalysisFormat$.write(TextAnalysisFormat.scala:216) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:64) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:64) at sbt.inc.TextAnalysisFormat21661anonfun.apply(TextAnalysisFormat.scala:64) at sbt.inc.FormatTimer$.aggregate(TextAnalysisFo at sbt.inc.TextAnalysisFormat21661anonfun.scala:25) at sbt.inc.TextAnalysisFormat$.write(TextAnalysisFormat.scala:64) at sbt.inc.FileBasedStore21661anon21661anonfun.apply(FileBasedStore.scala:12) at sbt.inc.FileBasedStore21661anon21661anonfun.apply(FileBasedStore.scala:12) at sbt.Using.apply(Using.scala:24) at sbt.inc.FileBasedStore21661anon.set(FileBasedStore.scala:12) at sbt.inc.AnalysisStore21661anon.set(AnalysisStore.scala:16) at sbt.inc.AnalysisStore21661anon.set(AnalysisStore.scala:27) at sbt.Defaults21661anonfun.apply(Defaults.scala:799) at sbt.Defaults21661anonfun at sbt.inc.TextAts.scala:793) at scala.Function121661anonfun.apply(Function1.scala:47) at sbt.21661anonfun21661u2219.apply(TypeFunctions.scala:40) at sbt.std.Transform21661anon.work(System.scala:63) at sbt.Execute21661anonfun21661anonfun.apply(Execute.scala:226) at sbt.Execute21661anonfun21661anonfun.apply(Execute.scala:226) at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17) at sbt.Execute.work(Execute.scala:235) at sbt.Execute21661anonfun.apply(Exe at sbt.Using.apply(Using.scala:24) mit.apply(Execute.scala:226) at sbt.ConcurrentRestrictions21661anon21661anonfun.apply(ConcurrentRestrictions.scala:159) at sbt.CompletionService21661anon.call(CompletionService.scala:28) at java.util.concurrent.FutureTask.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.Executors.call(Executors.java:439) at java.util.concurrent.FutureTask.innerRun(FutureTask.java:303 at sbt.std.Transform21661anon.work(System(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor.run(ThreadPoolExecutor.java:918) at java.lang.Thread.run(Thread.java:695) [error] (compile:compile) java.io.NotSerializableException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl [error] Total time: 2 s, completed Dec 21, 2015 12:05:19 PM ``` --- .../java-analysis-serialization-error/Outer.java | 1 + .../java-analysis-serialization-error/build.sbt | 1 + .../source-dependencies/java-analysis-serialization-error/test | 1 + 3 files changed, 3 insertions(+) create mode 100644 sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/Outer.java create mode 100644 sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt create mode 100644 sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/test diff --git a/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/Outer.java b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/Outer.java new file mode 100644 index 000000000..0cf8276a9 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/Outer.java @@ -0,0 +1 @@ +public class Outer { private class T extends Thread {} } \ No newline at end of file diff --git a/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt new file mode 100644 index 000000000..1b1ddefb9 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt @@ -0,0 +1 @@ +incOptions := incOptions.value.withNameHashing(true).withApiDebug(true) diff --git a/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/test b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/test new file mode 100644 index 000000000..5df2af1f3 --- /dev/null +++ b/sbt/src/sbt-test/source-dependencies/java-analysis-serialization-error/test @@ -0,0 +1 @@ +> compile From 95da6ec5a63614b1632cbd3b5db2edd745a9d027 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 21 Dec 2015 18:37:06 -0800 Subject: [PATCH 2/2] `AbstractLazy`'s `writeObject` is `protected` Fix for regression triggered by #2325 -- apparently, the API visitor would force all the lazy stubs so the `AbstractLazy` `writeReplace` was not exercised. With the private subtrees being ignored, the visitor didn't force the lazy stub, and the `writeProtected` method was not inherited in `SafeLazy.Impl`. From https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html `writeReplace` must be protected for its override to be inherited. > `Serializable` classes that need to designate an alternative object to be used > when writing an object to the stream should implement this special method with > the exact signature: > `ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;` > > This `writeReplace` method is invoked by serialization if the method exists and > it would be accessible from a method defined within the class of the object > being serialized. Thus, the method can have `private`, `protected` and > `package-private` access. > > **Subclass access to this method follows java accessibility rules.** (Thanks to retronym for digging up the docs.) The fix is captured, indirectly, by the scripted test `source-dependencies/java-analysis-serialization-error`. --- interface/src/main/java/xsbti/api/AbstractLazy.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/main/java/xsbti/api/AbstractLazy.java b/interface/src/main/java/xsbti/api/AbstractLazy.java index bd21f166f..bd49deded 100644 --- a/interface/src/main/java/xsbti/api/AbstractLazy.java +++ b/interface/src/main/java/xsbti/api/AbstractLazy.java @@ -3,11 +3,17 @@ */ package xsbti.api; - import java.io.ObjectStreamException; +import java.io.ObjectStreamException; public abstract class AbstractLazy implements Lazy, java.io.Serializable { - private Object writeReplace() throws ObjectStreamException + // `writeReplace` must be `protected`, so that the `Impl` subclass + // inherits the serialization override. + // + // (See source-dependencies/java-analysis-serialization-error, which would + // crash trying to serialize an AbstractLazy, because it pulled in an + // unserializable type eventually.) + protected Object writeReplace() throws ObjectStreamException { return new StrictLazy(get()); }