From 573c4bda0e4fb5af7ea20f021013c98139014365 Mon Sep 17 00:00:00 2001 From: jvican Date: Thu, 14 Sep 2017 12:59:12 +0200 Subject: [PATCH] Ignore null in generic lambda tparams `Method.getGenericParameterType` may sometimes return `null` for the return types when the method's return type is indeed generic. It's not clear yet where this "sometimes" happens, but it looks like the JDK is not able to tell the return type of a lambda returning the generic class. This can be seen in the `java-lambda-typeparams` scripted test. In this context, lambda metafactory synthesizes a lambda class that returns a class that is parameterized in its return type. In that context, when `ClassToAPI` inspects the synthesized lambda and tries to figure out the full return type of it, the `null` is returned. It looks like the Java reflection API sparingly returns `null`s. We can see that in `ClassToAPI` where we guard against `null`s in lots of other places. So the guard added by this commit is not a novelty, but rather the norm. Fixes #389. --- .../scala/sbt/internal/inc/ClassToAPI.scala | 21 ++++++++++----- .../java-lambda-typeparams/Example.java | 26 +++++++++++++++++++ .../java-lambda-typeparams/test | 1 + 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java create mode 100644 zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test diff --git a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala index 7301557394..4074c291a1 100644 --- a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala +++ b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala @@ -244,12 +244,14 @@ object ClassToAPI { accumulate(t).filterNot(_ == null).distinct } - @deprecated("No longer used", "0.13.0") - def parents(c: Class[_]): Seq[api.Type] = types(allSuperTypes(c)) - def types(ts: Seq[Type]): Array[api.Type] = (ts filter (_ ne null) map reference).toArray + def types(ts: Seq[Type]): Array[api.Type] = + ts.filter(_ ne null).map(reference).toArray def upperBounds(ts: Array[Type]): api.Type = api.Structure.of(lzy(types(ts)), lzyEmptyDefArray, lzyEmptyDefArray) + @deprecated("No longer used", "0.13.0") + def parents(c: Class[_]): Seq[api.Type] = types(allSuperTypes(c)) + @deprecated("Use fieldToDef[4] instead", "0.13.9") def fieldToDef(enclPkg: Option[String])(f: Field): api.FieldLike = { val c = f.getDeclaringClass @@ -496,12 +498,18 @@ object ClassToAPI { api.Projection.of(api.Singleton.of(pathFromString(p)), cls) } } + + // sbt/zinc#389: Ignore nulls coming from generic parameter types of lambdas + private[this] def ignoreNulls[T](genericTypes: Array[T]): Array[T] = + genericTypes.filter(_ != null) + def referenceP(t: ParameterizedType): api.Parameterized = { - val targs = t.getActualTypeArguments + val targs = ignoreNulls(t.getActualTypeArguments) val args = if (targs.isEmpty) emptyTypeArray else arrayMap(targs)(t => reference(t): api.Type) val base = reference(t.getRawType) api.Parameterized.of(base, args) } + def reference(t: Type): api.Type = t match { case _: WildcardType => reference("_") @@ -557,7 +565,7 @@ object ClassToAPI { case _: GenericSignatureFormatError => f.getType } private[this] def parameterTypes(c: Constructor[_]): Array[Type] = - try c.getGenericParameterTypes + try ignoreNulls(c.getGenericParameterTypes) catch { case _: GenericSignatureFormatError => convert(c.getParameterTypes) } @@ -567,7 +575,7 @@ object ClassToAPI { case _: GenericSignatureFormatError => convert(c.getExceptionTypes) } private[this] def parameterTypes(m: Method): Array[Type] = - try m.getGenericParameterTypes + try ignoreNulls(m.getGenericParameterTypes) catch { case _: GenericSignatureFormatError => convert(m.getParameterTypes) } @@ -581,7 +589,6 @@ object ClassToAPI { catch { case _: GenericSignatureFormatError => convert(m.getExceptionTypes) } - private[this] def typeParameterTypes[T](m: Constructor[T]): Array[TypeVariable[Constructor[T]]] = try m.getTypeParameters catch { diff --git a/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java new file mode 100644 index 0000000000..413bb68d59 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java @@ -0,0 +1,26 @@ +package typeparameters; + +import java.util.function.Supplier; + +public class Example { + + static void call() { + Supplier> blah = () -> + new BaseBlah() { + @Override + protected O getResponseInternal(I i) { + return null; + } + }; + } + + public static void main(String[] args) { + Example.call(); + } +} + +abstract class BaseBlah { + protected O getResponseInternal(I i) { + return null; + } +} \ No newline at end of file diff --git a/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test new file mode 100644 index 0000000000..73a68203f3 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test @@ -0,0 +1 @@ +> compile \ No newline at end of file