From 6328c555886f1c72035784dd4913627dfa8ec055 Mon Sep 17 00:00:00 2001 From: Googler Date: Mon, 16 Dec 2024 07:50:34 -0800 Subject: [PATCH] [J2KT] Create `InsertNotNullAssertionToPolyNullMethodCalls` pass which would insert not-null assertion to JRE method calls which are known to be `@PolyNull`. At this moment, there's only one method which needs that: `Optional.orElse()`. The existing `InsertNotNullAssertions` pass is renamed to `InsertNotNullAssertionsOnNullabilityMismatch`, for consistency with `InsertCastsOnNullabilityMismatch` pass. PiperOrigin-RevId: 706707865 --- .../j2cl/transpiler/ast/TypeDescriptors.java | 1 + .../j2cl/transpiler/backend/Backend.java | 6 +- ...NotNullAssertionToPolyNullMethodCalls.java | 58 ++++++++++++++++++ ...tNullAssertionsOnNullabilityMismatch.java} | 2 +- .../j2kt/NullabilityInferenceProblem.java | 7 +-- .../NullabilityInferenceProblem.kt.txt | 9 ++- .../NullabilityInferenceProblem.java | 41 ------------- .../NullabilityInferenceProblem.kt.txt | 61 ------------------- 8 files changed, 75 insertions(+), 110 deletions(-) create mode 100644 transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionToPolyNullMethodCalls.java rename transpiler/java/com/google/j2cl/transpiler/passes/{InsertNotNullAssertions.java => InsertNotNullAssertionsOnNullabilityMismatch.java} (98%) delete mode 100644 transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/NullabilityInferenceProblem.java delete mode 100644 transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/output_kt/NullabilityInferenceProblem.kt.txt diff --git a/transpiler/java/com/google/j2cl/transpiler/ast/TypeDescriptors.java b/transpiler/java/com/google/j2cl/transpiler/ast/TypeDescriptors.java index e7b2858c03..43d15c8691 100644 --- a/transpiler/java/com/google/j2cl/transpiler/ast/TypeDescriptors.java +++ b/transpiler/java/com/google/j2cl/transpiler/ast/TypeDescriptors.java @@ -88,6 +88,7 @@ public class TypeDescriptors { public DeclaredTypeDescriptor javaUtilMap; public DeclaredTypeDescriptor javaUtilList; public DeclaredTypeDescriptor javaUtilObjects; + public DeclaredTypeDescriptor javaUtilOptional; @Nullable @QualifiedBinaryName("java.util.ReadonlyCollection") diff --git a/transpiler/java/com/google/j2cl/transpiler/backend/Backend.java b/transpiler/java/com/google/j2cl/transpiler/backend/Backend.java index 1cf54ea32e..38844c77f3 100644 --- a/transpiler/java/com/google/j2cl/transpiler/backend/Backend.java +++ b/transpiler/java/com/google/j2cl/transpiler/backend/Backend.java @@ -82,7 +82,8 @@ import com.google.j2cl.transpiler.passes.InsertNarrowingPrimitiveConversions; import com.google.j2cl.transpiler.passes.InsertNarrowingPrimitiveConversionsJ2kt; import com.google.j2cl.transpiler.passes.InsertNarrowingReferenceConversions; -import com.google.j2cl.transpiler.passes.InsertNotNullAssertions; +import com.google.j2cl.transpiler.passes.InsertNotNullAssertionToPolyNullMethodCalls; +import com.google.j2cl.transpiler.passes.InsertNotNullAssertionsOnNullabilityMismatch; import com.google.j2cl.transpiler.passes.InsertQualifierProjectionCasts; import com.google.j2cl.transpiler.passes.InsertRawTypeCasts; import com.google.j2cl.transpiler.passes.InsertStringConversions; @@ -759,7 +760,8 @@ public ImmutableList> getPassFactories(BackendOption NormalizePrimitiveCastsJ2kt::new, ImplementBitLevelOperatorsJ2kt::new, InsertQualifierProjectionCasts::new, - InsertNotNullAssertions::new, + InsertNotNullAssertionToPolyNullMethodCalls::new, + InsertNotNullAssertionsOnNullabilityMismatch::new, InsertCastsOnNullabilityMismatch::new, InsertCastForLowerBounds::new, InsertRawTypeCasts::new, diff --git a/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionToPolyNullMethodCalls.java b/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionToPolyNullMethodCalls.java new file mode 100644 index 0000000000..35ded431a5 --- /dev/null +++ b/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionToPolyNullMethodCalls.java @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.j2cl.transpiler.passes; + +import com.google.j2cl.transpiler.ast.AbstractRewriter; +import com.google.j2cl.transpiler.ast.CompilationUnit; +import com.google.j2cl.transpiler.ast.MethodCall; +import com.google.j2cl.transpiler.ast.MethodDescriptor; +import com.google.j2cl.transpiler.ast.Node; +import com.google.j2cl.transpiler.ast.TypeDescriptors; + +/** + * Inserts not-null assertion to method calls which are known to be @PolyNull, where the argument + * can not be null. + */ +public class InsertNotNullAssertionToPolyNullMethodCalls extends NormalizationPass { + private final MethodDescriptor javaUtilOptionalOrElse = + TypeDescriptors.get() + .javaUtilOptional + .getMethodDescriptor("orElse", TypeDescriptors.get().javaLangObject); + + @Override + public void applyTo(CompilationUnit compilationUnit) { + compilationUnit.accept( + new AbstractRewriter() { + @Override + public Node rewriteMethodCall(MethodCall methodCall) { + MethodDescriptor methodDescriptor = methodCall.getTarget(); + if (isOrOverrides(methodDescriptor, javaUtilOptionalOrElse)) { + if (!methodCall.getArguments().get(0).canBeNull()) { + return methodCall.postfixNotNullAssertion(); + } + } + return methodCall; + } + }); + } + + private static boolean isOrOverrides( + MethodDescriptor methodDescriptor, MethodDescriptor otherMethodDescriptor) { + return methodDescriptor.getDeclarationDescriptor().equals(otherMethodDescriptor) + || methodDescriptor.getJavaOverriddenMethodDescriptors().stream() + .anyMatch(it -> it.getDeclarationDescriptor().equals(otherMethodDescriptor)); + } +} diff --git a/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertions.java b/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionsOnNullabilityMismatch.java similarity index 98% rename from transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertions.java rename to transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionsOnNullabilityMismatch.java index 5be037b108..a4a51d7b34 100644 --- a/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertions.java +++ b/transpiler/java/com/google/j2cl/transpiler/passes/InsertNotNullAssertionsOnNullabilityMismatch.java @@ -37,7 +37,7 @@ * Inserts NOT_NULL_ASSERTION (!!) in places where Java performs implicit null-check, and when * conversion is needed from nullable to non-null type. */ -public final class InsertNotNullAssertions extends NormalizationPass { +public final class InsertNotNullAssertionsOnNullabilityMismatch extends NormalizationPass { @Override public void applyTo(CompilationUnit compilationUnit) { // Insert non-null assertions when converting from nullable to non-null type. diff --git a/transpiler/javatests/com/google/j2cl/readable/java/j2kt/NullabilityInferenceProblem.java b/transpiler/javatests/com/google/j2cl/readable/java/j2kt/NullabilityInferenceProblem.java index 1b3d5b4273..5b822421d2 100644 --- a/transpiler/javatests/com/google/j2cl/readable/java/j2kt/NullabilityInferenceProblem.java +++ b/transpiler/javatests/com/google/j2cl/readable/java/j2kt/NullabilityInferenceProblem.java @@ -40,10 +40,9 @@ private static T checkNotNull(@Nullable T reference) { throw new RuntimeException(); } - // TODO(b/383581076): Uncomment when fixed. - // private static ImmutableList testImplicitTypeArguments(Iterable users) { - // return sortedCopyOf(comparing(user -> user.getName().orElse("")), users); - // } + private static ImmutableList testImplicitTypeArguments(Iterable users) { + return sortedCopyOf(comparing(user -> user.getName().orElse("")), users); + } private static ImmutableList testExplicitTypeArguments(Iterable users) { return sortedCopyOf( diff --git a/transpiler/javatests/com/google/j2cl/readable/java/j2kt/output_kt/NullabilityInferenceProblem.kt.txt b/transpiler/javatests/com/google/j2cl/readable/java/j2kt/output_kt/NullabilityInferenceProblem.kt.txt index 4344aab4a5..5956f96b73 100644 --- a/transpiler/javatests/com/google/j2cl/readable/java/j2kt/output_kt/NullabilityInferenceProblem.kt.txt +++ b/transpiler/javatests/com/google/j2cl/readable/java/j2kt/output_kt/NullabilityInferenceProblem.kt.txt @@ -47,6 +47,13 @@ open class NullabilityInferenceProblem internal constructor() { throw RuntimeException() } + @JvmStatic + private fun testImplicitTypeArguments(users: MutableIterable): NullabilityInferenceProblem.ImmutableList { + return NullabilityInferenceProblem.sortedCopyOf(java.util.Comparator.comparing(Function { user: NullabilityInferenceProblem.User -> + return@Function user.getName().orElse("")!! + }), users) + } + @JvmStatic private fun testExplicitTypeArguments(users: MutableIterable): NullabilityInferenceProblem.ImmutableList { return NullabilityInferenceProblem.sortedCopyOf(java.util.Comparator.comparing(Function { user: NullabilityInferenceProblem.User -> @@ -57,7 +64,7 @@ open class NullabilityInferenceProblem internal constructor() { @JvmStatic private fun testCheckNotNull(users: MutableIterable): NullabilityInferenceProblem.ImmutableList { return NullabilityInferenceProblem.sortedCopyOf(java.util.Comparator.comparing(Function { user: NullabilityInferenceProblem.User -> - return@Function NullabilityInferenceProblem.checkNotNull(user.getName().orElse("")) + return@Function NullabilityInferenceProblem.checkNotNull(user.getName().orElse("")!!) }), users) } } diff --git a/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/NullabilityInferenceProblem.java b/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/NullabilityInferenceProblem.java deleted file mode 100644 index cb15d356ac..0000000000 --- a/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/NullabilityInferenceProblem.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package j2ktnotpassing; - -import static java.util.Comparator.comparing; - -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import org.jspecify.annotations.NullMarked; - -@NullMarked -class NullabilityInferenceProblem { - interface ImmutableList extends List {} - - interface User { - Optional getName(); - } - - private static ImmutableList sortedCopyOf( - Comparator comparator, Iterable elements) { - throw new RuntimeException(); - } - - private static ImmutableList testImplicitTypeArguments(Iterable users) { - return sortedCopyOf(comparing(user -> user.getName().orElse("")), users); - } -} diff --git a/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/output_kt/NullabilityInferenceProblem.kt.txt b/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/output_kt/NullabilityInferenceProblem.kt.txt deleted file mode 100644 index 7c7bb85c67..0000000000 --- a/transpiler/javatests/com/google/j2cl/readable/java/j2ktnotpassing/output_kt/NullabilityInferenceProblem.kt.txt +++ /dev/null @@ -1,61 +0,0 @@ -// Generated from "j2ktnotpassing/NullabilityInferenceProblem.java" -@file:OptIn(ExperimentalObjCName::class) -@file:Suppress( - "ALWAYS_NULL", - "PARAMETER_NAME_CHANGED_ON_OVERRIDE", - "SENSELESS_COMPARISON", - "UNCHECKED_CAST", - "UNNECESSARY_LATEINIT", - "UNNECESSARY_NOT_NULL_ASSERTION", - "UNREACHABLE_CODE", - "UNUSED_ANONYMOUS_PARAMETER", - "UNUSED_PARAMETER", - "UNUSED_VARIABLE", - "USELESS_CAST", - "VARIABLE_IN_SINGLETON_WITHOUT_THREAD_LOCAL", - "VARIABLE_WITH_REDUNDANT_INITIALIZER", - "REDUNDANT_ELSE_IN_WHEN") - -package j2ktnotpassing - -import javaemul.lang.* -import java.lang.RuntimeException -import java.util.Optional -import java.util.function.Function -import javaemul.lang.JavaList -import kotlin.Any -import kotlin.Comparator -import kotlin.OptIn -import kotlin.String -import kotlin.Suppress -import kotlin.collections.MutableIterable -import kotlin.experimental.ExperimentalObjCName -import kotlin.jvm.JvmStatic -import kotlin.native.ObjCName - -@ObjCName("J2ktJ2ktnotpassingNullabilityInferenceProblem", exact = true) -open class NullabilityInferenceProblem internal constructor() { - @ObjCName("J2ktJ2ktnotpassingNullabilityInferenceProblemCompanion", exact = true) - companion object { - @JvmStatic - private fun sortedCopyOf(comparator: Comparator, elements: MutableIterable): NullabilityInferenceProblem.ImmutableList { - throw RuntimeException() - } - - @JvmStatic - private fun testImplicitTypeArguments(users: MutableIterable): NullabilityInferenceProblem.ImmutableList { - return NullabilityInferenceProblem.sortedCopyOf(java.util.Comparator.comparing(Function { user: NullabilityInferenceProblem.User -> - return@Function user.getName().orElse("") - }), users) - } - } - - @ObjCName("J2ktJ2ktnotpassingNullabilityInferenceProblem_ImmutableList", exact = true) - interface ImmutableList: JavaList {} - - @ObjCName("J2ktJ2ktnotpassingNullabilityInferenceProblem_User", exact = true) - fun interface User { - @ObjCName("getName") - fun getName(): Optional - } -}