[K/JS] Use declared upper-bound types for parameters inside inlined functions body, instead of the provided types

This commit is contained in:
Artem Kobzar
2023-06-20 12:01:28 +00:00
committed by Space Team
parent aa4ebe1991
commit 039b5fca7a
38 changed files with 269 additions and 53 deletions
@@ -79,6 +79,9 @@ var JsReturn.returnTarget: FunctionDescriptor? by MetadataProperty(default = nul
var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
var HasMetadata.isInlineClassBoxing: Boolean by MetadataProperty(default = false)
var HasMetadata.isInlineClassUnboxing: Boolean by MetadataProperty(default = false)
var HasMetadata.sideEffects: SideEffectKind by MetadataProperty(default = SideEffectKind.AFFECTS_STATE)
/**
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.js.inline.clean
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsExpression.JsExpressionHasArguments
import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassBoxing
import org.jetbrains.kotlin.js.backend.ast.metadata.isInlineClassUnboxing
import org.jetbrains.kotlin.js.backend.ast.metadata.isJsCall
import org.jetbrains.kotlin.js.inline.util.isCallInvocation
// Replaces box(unbox(value)) and unbox(box(value)) with value
class BoxingUnboxingElimination(private val root: JsBlock) {
private var changed = false
fun apply(): Boolean {
val visitor = object : JsVisitorWithContextImpl() {
override fun endVisit(x: JsInvocation, ctx: JsContext<JsNode>) {
super.endVisit(x, ctx)
tryEliminate(x, ctx)
}
override fun endVisit(x: JsNew, ctx: JsContext<JsNode>) {
super.endVisit(x, ctx)
tryEliminate(x, ctx)
}
override fun endVisit(x: JsNameRef, ctx: JsContext<JsNode>) {
super.endVisit(x, ctx)
tryEliminate(x, ctx)
}
override fun endVisit(x: JsArrayAccess, ctx: JsContext<*>) {
super.endVisit(x, ctx)
}
override fun visit(x: JsFunction, ctx: JsContext<JsNode>) = false
private fun tryEliminate(expression: JsExpression, ctx: JsContext<JsNode>) {
if (!expression.isInlineClassBoxing && !expression.isInlineClassUnboxing) return
val firstArg = expression.arguments.first()
if (!firstArg.isInlineClassBoxing && !firstArg.isInlineClassUnboxing) return
if (firstArg.isInlineClassBoxing != expression.isInlineClassBoxing) {
ctx.replaceMe(firstArg.arguments.first())
changed = true
}
}
private val JsExpression.arguments: List<JsExpression>
get() = when (this) {
is JsExpressionHasArguments -> arguments
is JsNameRef -> listOfNotNull(qualifier)
else -> emptyList()
}
}
visitor.accept(root)
return changed
}
}
@@ -31,7 +31,8 @@ class FunctionPostProcessor(val root: JsFunction) {
{ DeadCodeElimination(root.body).apply() },
{ RedundantVariableDeclarationElimination(root.body).apply() },
{ RedundantStatementElimination(root).apply() },
{ CoroutineStateElimination(root.body).apply() }
{ CoroutineStateElimination(root.body).apply() },
{ BoxingUnboxingElimination(root.body).apply() }
)
// TODO: reduce to A || B, A && B if possible
@@ -1692,6 +1692,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/dynamic/iterator.kt");
}
@Test
@TestMetadata("lambdaParameterInlining.kt")
public void testLambdaParameterInlining() throws Exception {
runTest("js/js.translator/testData/box/dynamic/lambdaParameterInlining.kt");
}
@Test
@TestMetadata("nameClashing.kt")
public void testNameClashing() throws Exception {
@@ -1762,6 +1762,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
runTest("js/js.translator/testData/box/dynamic/iterator.kt");
}
@Test
@TestMetadata("lambdaParameterInlining.kt")
public void testLambdaParameterInlining() throws Exception {
runTest("js/js.translator/testData/box/dynamic/lambdaParameterInlining.kt");
}
@Test
@TestMetadata("operationsWithAssignment.kt")
public void testOperationsWithAssignment() throws Exception {
@@ -18570,6 +18570,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/kt37986Generic.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Test
@TestMetadata("withInlineFunctionWithMultipleConstraints.kt")
public void testWithInlineFunctionWithMultipleConstraints() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/withInlineFunctionWithMultipleConstraints.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/callableReferences/let")
@TestDataPath("$PROJECT_ROOT")
@@ -1762,6 +1762,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
runTest("js/js.translator/testData/box/dynamic/iterator.kt");
}
@Test
@TestMetadata("lambdaParameterInlining.kt")
public void testLambdaParameterInlining() throws Exception {
runTest("js/js.translator/testData/box/dynamic/lambdaParameterInlining.kt");
}
@Test
@TestMetadata("operationsWithAssignment.kt")
public void testOperationsWithAssignment() throws Exception {
@@ -1762,6 +1762,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/dynamic/iterator.kt");
}
@Test
@TestMetadata("lambdaParameterInlining.kt")
public void testLambdaParameterInlining() throws Exception {
runTest("js/js.translator/testData/box/dynamic/lambdaParameterInlining.kt");
}
@Test
@TestMetadata("operationsWithAssignment.kt")
public void testOperationsWithAssignment() throws Exception {
@@ -18570,6 +18570,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/kt37986Generic.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Test
@TestMetadata("withInlineFunctionWithMultipleConstraints.kt")
public void testWithInlineFunctionWithMultipleConstraints() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/withInlineFunctionWithMultipleConstraints.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/callableReferences/let")
@TestDataPath("$PROJECT_ROOT")
@@ -18570,6 +18570,12 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/kt37986Generic.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Test
@TestMetadata("withInlineFunctionWithMultipleConstraints.kt")
public void testWithInlineFunctionWithMultipleConstraints() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/callableReferences/withInlineFunctionWithMultipleConstraints.kt", TransformersFunctions.getRemoveOptionalJvmInlineAnnotation());
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/callableReferences/let")
@TestDataPath("$PROJECT_ROOT")
@@ -1,4 +1,5 @@
// EXPECTED_REACHABLE_NODES: 1288
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
@JsExport
interface I {
@@ -1,4 +1,6 @@
// EXPECTED_REACHABLE_NODES: 1282
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
fun box(): String {
val a = 'Q'.foo()
if (a != "number") return "fail1: $a"
@@ -1,4 +1,5 @@
// EXPECTED_REACHABLE_NODES: 1289
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
@JsExport
open class A {
@@ -1,4 +1,6 @@
// EXPECTED_REACHABLE_NODES: 1283
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
fun foo(x: Any): String {
return when (x) {
is Char -> "char: ${x.toInt()}"
@@ -0,0 +1,17 @@
// WITH_STDLIB
fun demo(x: dynamic, a: Array<out dynamic>): Boolean? {
return a.any { y: Any ->
val newX: Any = x.unsafeCast<Any>()
y == newX
}
}
data class X(val x: Int)
fun box(): String {
if (demo(X(1), arrayOf(X(1))) != true) return "fail"
return "OK"
}
+1 -2
View File
@@ -317,8 +317,7 @@ fun testCompareDifferentInstancesInSmartCast() {
fun testCompareDifferentInstncesInInlineTemplate() {
inline fun <reified T, reified S> myEq(x: T, y: S) = x == y
// CHECK_NOT_CALLED_IN_SCOPE: scope=testCompareDifferentInstncesInInlineTemplate$caseJsEq function=equals
// CHECK_NEW_COUNT: function=testCompareDifferentInstncesInInlineTemplate$caseJsEq count=0
// CHECK_NEW_COUNT: function=testCompareDifferentInstncesInInlineTemplate$caseJsEq count=8
fun caseJsEq() {
assertTrue(myEq(ClassInt(1), ClassInt(1)))
assertTrue(myEq(ClassString("foo"), ClassString("foo")))
+1 -2
View File
@@ -2,8 +2,7 @@
package foo
// CHECK_NOT_CALLED: isTypeOfOrNull
// CHECK_NULLS_COUNT: function=box count=10 TARGET_BACKENDS=JS
// CHECK_NULLS_COUNT: function=box count=6 IGNORED_BACKENDS=JS
// CHECK_NULLS_COUNT: function=box count=10
inline
fun <reified T> Any?.isTypeOfOrNull() = this is T?
@@ -1,4 +1,5 @@
// EXPECTED_REACHABLE_NODES: 1281
// IGNORE_BACKEND: JS_IR, JS_IR_ES6
fun box(): String {
val a = CharArray(1)
+2 -1
View File
@@ -15,4 +15,5 @@ suspend fun bar(): Unit {
}
// LINES(JS): 39 4 4 4 7 5 5 45 45 5 93 45 5 5 6 4 4 4 9 15 9 9 9 * 9 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 9 15 9 9 9 9
// LINES(JS_IR): 4 4 * 93 93 45 45 7 7 6 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15
// LINES(JS_IR): 4 4 * 93 93 3 45 45 7 7 6 9 9 * 9 * 9 * 10 10 * 11 * 11 12 12 * 13 * 13 14 14 15 15
@@ -33,4 +33,4 @@ inline operator fun P.component1() = a
inline operator fun P.component2() = b
// LINES(JS): 15 22 17 17 31 18 18 33 20 20 21 21 22 22 3 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 25 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 9 9 4 9 * 6 31 * 7 33 11 11 12 12 15 15 25 25 26 26 29 29 29 29 29 29 29 29 29 29 29 29 31 31 31 31 33 33 33 33 15 16 15 * 17 31 * 18 33 20 20 21 21 22 22 * 1
// LINES(JS_IR): 1 1 1 1 1 1 1 1 * 3 3 9 9 4 9 * 6 31 9 * 7 33 9 11 11 12 12 15 15 25 25 26 26 29 29 29 29 29 29 29 29 29 29 29 29 31 31 31 31 33 33 33 33 15 16 15 * 17 31 16 * 18 33 16 20 20 21 21 22 22 * 1
+3 -2
View File
@@ -17,5 +17,6 @@ fun box() {
}
}
// LINES(JS): 1 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
// LINES(JS_IR): 1 1 * 2 2 2 2 2 2 2 2 3 3 6 6 6 6 6 6 6 7 7 10 10 10 10 11 11 14 15 15 15 15 15 15 15 15 16 16
// LINES(JS): 1 18 2 2 10 2 2 2 2 2 2 3 3 6 6 6 6 7 7 10 10 10 10 10 10 11 11 14 14 15 15 15 15 16 16
// LINES(JS_IR): 1 1 * 2 2 2 2 2 2 2 2 3 3 6 6 6 6 6 6 6 7 7 10 10 10 10 11 11 14 15 15 15 15 15 15 15 15 16 16
@@ -10,4 +10,4 @@ fun bar() {
}
// LINES(JS): 1 1 1 1 1 6 2 2 3 3 4 4 8 10 2 2 9 2 3 3 4 4
// LINES(JS_IR): 1 1 2 3 3 3 4 4 8 8 * 2 3 3 3 4 4
// LINES(JS_IR): 1 1 2 3 3 3 4 4 8 8 * 2 9 2 3 3 3 4 4
@@ -22,5 +22,5 @@ fun box() {
foo("42")
}
// LINES(JS): 6 20 23 7 7 21 7 8 8 21 8 7 7 22 7 8 8 22 8
// LINES(JS_IR): 20 20 * 7 7 8 8 * 7 7 8 8
// LINES(JS): 6 20 23 7 7 21 7 8 8 21 8 7 7 22 7 8 8 22 8
// LINES(JS_IR): 20 20 * 7 7 8 7 8 8 * 7 7 8 7 8 8