From 9716abcc120c02ddd22d739c84efb6076b498ec1 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Sun, 23 Dec 2018 21:27:57 +0300 Subject: [PATCH] JS: inline private properties --- .../js/inline/context/FunctionContext.kt | 23 ++- .../kotlin/js/inline/util/collectUtils.kt | 30 ++- .../js/test/semantics/BoxJsTestGenerated.java | 5 + .../test/semantics/IrBoxJsTestGenerated.java | 5 + .../testData/box/inline/privateProperty.kt | 190 ++++++++++++++++++ 5 files changed, 245 insertions(+), 8 deletions(-) create mode 100644 js/js.translator/testData/box/inline/privateProperty.kt diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt index 3c76f683f29..78456cf9c9b 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionContext.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.js.inline.context +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor @@ -83,11 +84,12 @@ class FunctionContext( private val functionReader = FunctionReader(inliner.reporter, inliner.config, inliner.translationResult.innerModuleName) - private data class FunctionsAndAccessors( + private data class FragmentInfo( val functions: Map, - val accessors: Map) + val accessors: Map, + val localAccessors: Map) - private val fragmentInfo = mutableMapOf() + private val fragmentInfo = mutableMapOf() private fun lookUpStaticFunction(functionName: JsName?, fragment: JsProgramFragment): FunctionWithWrapper? = fragmentInfo[fragment]?.run { functions[functionName] } @@ -104,6 +106,16 @@ class FunctionContext( call.qualifier } + call.descriptor?.let { descriptor -> + fragmentInfo[scope.fragment]?.let { info -> + info.localAccessors[descriptor]?.let { fn -> + return InlineFunctionDefinition(fn, null).also { def -> + inliner.process(def, call, scope) + } + } + } + } + /** process cases 2, 3 */ val qualifier = callQualifier.transitiveStaticRef return when (qualifier) { @@ -134,9 +146,10 @@ class FunctionContext( private fun loadFragment(fragment: JsProgramFragment) { fragmentInfo.computeIfAbsent(fragment) { - FunctionsAndAccessors( + FragmentInfo( collectNamedFunctionsAndWrappers(listOf(fragment)), - collectAccessors(listOf(fragment)) + collectAccessors(listOf(fragment)), + collectLocalFunctions(listOf(fragment)) ).also { (functions, accessors) -> (functions.values.asSequence() + accessors.values.asSequence()).forEach { f -> functionsByFunctionNodes[f.function] = f diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt index 0ae35d32bc1..a033c01352d 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt @@ -16,13 +16,12 @@ package org.jetbrains.kotlin.js.inline.util +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor import org.jetbrains.kotlin.js.backend.ast.* -import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor +import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor import org.jetbrains.kotlin.js.backend.ast.metadata.imported -import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector -import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.expression.InlineMetadata import org.jetbrains.kotlin.js.translate.utils.JsAstUtils @@ -248,6 +247,31 @@ fun collectAccessors(fragments: Iterable): Map { + val localFunctions = hashMapOf() + + scope.accept(object : RecursiveJsVisitor() { + override fun visitInvocation(invocation: JsInvocation) { + InlineMetadata.tryExtractFunction(invocation)?.let { + it.function.functionDescriptor?.let { fd -> + localFunctions[fd] = it + } + } + super.visitInvocation(invocation) + } + }) + + return localFunctions +} + +fun collectLocalFunctions(fragments: List): Map { + val result = mutableMapOf() + for (fragment in fragments) { + result += collectLocalFunctions(fragment.declarationBlock) + } + return result +} + fun extractFunction(expression: JsExpression) = when (expression) { is JsFunction -> FunctionWithWrapper(expression, null) else -> InlineMetadata.decompose(expression)?.function ?: InlineMetadata.tryExtractFunction(expression) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 19c2b374d99..ff330604cae 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -4110,6 +4110,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/inline/params.kt"); } + @TestMetadata("privateProperty.kt") + public void testPrivateProperty() throws Exception { + runTest("js/js.translator/testData/box/inline/privateProperty.kt"); + } + @TestMetadata("rootConstructor.kt") public void testRootConstructor() throws Exception { runTest("js/js.translator/testData/box/inline/rootConstructor.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java index 9b7e6fd8f57..1e0950d0637 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java @@ -4110,6 +4110,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/inline/params.kt"); } + @TestMetadata("privateProperty.kt") + public void testPrivateProperty() throws Exception { + runTest("js/js.translator/testData/box/inline/privateProperty.kt"); + } + @TestMetadata("rootConstructor.kt") public void testRootConstructor() throws Exception { runTest("js/js.translator/testData/box/inline/rootConstructor.kt"); diff --git a/js/js.translator/testData/box/inline/privateProperty.kt b/js/js.translator/testData/box/inline/privateProperty.kt new file mode 100644 index 00000000000..d3765e1efea --- /dev/null +++ b/js/js.translator/testData/box/inline/privateProperty.kt @@ -0,0 +1,190 @@ +// EXPECTED_REACHABLE_NODES: 1288 +package test + +var a = 0 + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p1 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p1 scope=box +private inline var p1: Int + get() = a + 10000 + set(v) { + a = v + 100 + } + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p2 scope=box +// CHECK_CALLED_IN_SCOPE: function=set_p2 scope=box +private var p2: Int + inline get() = a + 20000 + set(v) { + a = v + 200 + } + +// CHECK_CALLED_IN_SCOPE: function=get_p3 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p3 scope=box +var p3: Int + get() = a + 30000 + private inline set(v) { + a = v + 300 + } + +// CHECK_CALLED_IN_SCOPE: function=get_p4 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p4 scope=box +private var p4: Int + get() = a + 40000 + inline set(v) { + a = v + 400 + } + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p5 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p5 scope=box +private inline var Int.p5: Int + get() = this * 100 + a + 50000 + set(v) { + a = this + v + 500 + } + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p6 scope=box +// CHECK_CALLED_IN_SCOPE: function=set_p6 scope=box +private var Int.p6: Int + inline get() = this * 100 + a + 60000 + set(v) { + a = this + v + 600 + } + +// CHECK_CALLED_IN_SCOPE: function=get_p7 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p7 scope=box +var Int.p7: Int + get() = this * 100 + a + 70000 + private inline set(v) { + a = this + v + 700 + } + +// CHECK_CALLED_IN_SCOPE: function=get_p8 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p8 scope=box +private var Int.p8: Int + get() = this * 100 + a + 80000 + inline set(v) { + a = this + v + 800 + } + + +private class A { + // PROPERTY_NOT_USED: p9 + inline var p9: Int + get() = a + 90000 + set(v) { + a = v + 900 + } + + // PROPERTY_NOT_READ_FROM: p10 + var p10: Int + inline get() = a + 100000 + set(v) { + a = v + 1000 + } + + // PROPERTY_NOT_WRITTEN_TO: p11 + var p11: Int + get() = a + 110000 + inline set(v) { + a = v + 1100 + } + + // CHECK_NOT_CALLED_IN_SCOPE: function=get_p12_s8ev3n$ scope=box + // CHECK_NOT_CALLED_IN_SCOPE: function=set_p12_dqglrj$ scope=box + inline var Int.p12: Int + get() = this * 100 + a + 120000 + set(v) { + a = this + v + 1200 + } + + // CHECK_NOT_CALLED_IN_SCOPE: function=get_p13_s8ev3n$ scope=box + // CHECK_CALLED_IN_SCOPE: function=set_p13_dqglrj$ scope=box + var Int.p13: Int + inline get() = this * 100 + a + 130000 + set(v) { + a = this + v + 1300 + } + + // CHECK_CALLED_IN_SCOPE: function=get_p14_s8ev3n$ scope=box + // CHECK_NOT_CALLED_IN_SCOPE: function=set_p14_dqglrj$ scope=box + var Int.p14: Int + get() = this * 100 + a + 140000 + inline set(v) { + a = this + v + 1400 + } +} + + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p15 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p15 scope=box +private inline var A.p15: Int + get() = a + 150000 + set(v) { + a = v + 1500 + } + +// CHECK_NOT_CALLED_IN_SCOPE: function=get_p16 scope=box +// CHECK_CALLED_IN_SCOPE: function=set_p16 scope=box +private var A.p16: Int + inline get() = a + 160000 + set(v) { + a = v + 1600 + } + +// CHECK_CALLED_IN_SCOPE: function=get_p17 scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=set_p17 scope=box +private var A.p17: Int + get() = a + 170000 + inline set(v) { + a = v + 1700 + } + + +fun box(): String { + p1 = 1 + if (p1 != 10101) return "test1: $p1" + p2 = 2 + if (p2 != 20202) return "test2: $p2" + p3 = 3 + if (p3 != 30303) return "test3: $p3" + p4 = 4 + if (p4 != 40404) return "test4: $p4" + + 5000000.p5 = 5 + if (5000000.p5 != 505050505) return "test5: ${5000000.p5}" + 6000000.p6 = 6 + if (6000000.p6 != 606060606) return "test6: ${6000000.p6}" + 7000000.p7 = 7 + if (7000000.p7 != 707070707) return "test7: ${7000000.p7}" + 8000000.p8 = 8 + if (8000000.p8 != 808080808) return "test8: ${8000000.p8}" + + + val a = A() + + a.p9 = 9 + if (a.p9 != 90909) return "test9: ${a.p9}" + a.p10 = 10 + if (a.p10 != 101010) return "test10: ${a.p10}" + a.p11 = 11 + if (a.p11 != 111111) return "test11: ${a.p11}" + + with (a) { + 12000000.p12 = 12 + if (12000000.p12 != 1212121212) return "test12: ${12000000.p12}" + 13000000.p13 = 13 + if (13000000.p13 != 1313131313) return "test13: ${13000000.p13}" + 14000000.p14 = 14 + if (14000000.p14 != 1414141414) return "test14: ${14000000.p14}" + } + + a.p15 = 15 + if (a.p15 != 151515) return "test15: ${a.p15}" + a.p16 = 16 + if (a.p16 != 161616) return "test16: ${a.p16}" + a.p17 = 17 + if (a.p17 != 171717) return "test17: ${a.p17}" + + return "OK" +} \ No newline at end of file