API version calls preprocessing, 1st passing test
This commit is contained in:
@@ -263,7 +263,8 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + sourceCompiler.callElementText,
|
||||
createNestedSourceMapper(nodeAndSmap, defaultSourceMapper), info.callSiteInfo,
|
||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null
|
||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
!isInlinedToInlineFunInKotlinRuntime()
|
||||
) //with captured
|
||||
|
||||
val remapper = LocalVarRemapper(parameters, initialFrameSize)
|
||||
@@ -297,6 +298,14 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
|
||||
val codegen = this.codegen as? ExpressionCodegen ?: return false
|
||||
val caller = codegen.context.functionDescriptor
|
||||
if (!caller.isInline) return false
|
||||
val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false
|
||||
return callerPackage.fqName.asString().startsWith("kotlin.")
|
||||
}
|
||||
|
||||
private fun generateClosuresBodies() {
|
||||
for (info in expressionMap.values) {
|
||||
info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.ClosureCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
@@ -46,7 +47,8 @@ class MethodInliner(
|
||||
private val errorPrefix: String,
|
||||
private val sourceMapper: SourceMapper,
|
||||
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper? //non null only for root
|
||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
|
||||
private val shouldPreprocessApiVersionCalls: Boolean = false
|
||||
) {
|
||||
private val typeMapper = inliningContext.state.typeMapper
|
||||
private val invokeCalls = ArrayList<InvokeCall>()
|
||||
@@ -394,7 +396,7 @@ class MethodInliner(
|
||||
): MethodNode {
|
||||
val processingNode = prepareNode(node, finallyDeepShift)
|
||||
|
||||
normalizeNodeBeforeInline(processingNode, labelOwner)
|
||||
preprocessNodeBeforeInline(processingNode, labelOwner)
|
||||
|
||||
val sources = analyzeMethodNodeBeforeInline(processingNode)
|
||||
|
||||
@@ -508,7 +510,7 @@ class MethodInliner(
|
||||
return processingNode
|
||||
}
|
||||
|
||||
private fun normalizeNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
|
||||
try {
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
}
|
||||
@@ -516,6 +518,11 @@ class MethodInliner(
|
||||
throw wrapException(e, node, "couldn't inline method call")
|
||||
}
|
||||
|
||||
if (shouldPreprocessApiVersionCalls) {
|
||||
val targetApiVersion = inliningContext.state.languageVersionSettings.apiVersion
|
||||
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
|
||||
}
|
||||
|
||||
val frames = analyzeMethodNodeBeforeInline(node)
|
||||
|
||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isMethodInsnWith
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.MavenComparableVersion
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion: ApiVersion) : MethodTransformer() {
|
||||
private val constantConditionElimination = ConstantConditionEliminationMethodTransformer()
|
||||
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
for (insn in methodNode.instructions.toArray()) {
|
||||
if (!insn.isApiVersionIsAtLeastCall()) continue
|
||||
|
||||
val prev3 = insn.previous ?: continue
|
||||
val minor = prev3.getIntConstValue() ?: continue
|
||||
|
||||
val prev2 = prev3.previous ?: continue
|
||||
val major = prev2.getIntConstValue() ?: continue
|
||||
|
||||
val prev1 = prev2.previous ?: continue
|
||||
val epic = prev1.getIntConstValue() ?: continue
|
||||
|
||||
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
|
||||
|
||||
val replacementInsn =
|
||||
if (targetApiVersion.version >= atLeastVersion)
|
||||
InsnNode(Opcodes.ICONST_1)
|
||||
else
|
||||
InsnNode(Opcodes.ICONST_0)
|
||||
|
||||
methodNode.instructions.run {
|
||||
remove(prev1)
|
||||
remove(prev2)
|
||||
remove(prev3)
|
||||
set(insn, replacementInsn)
|
||||
}
|
||||
}
|
||||
|
||||
constantConditionElimination.transform(internalClassName, methodNode)
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner.startsWith("kotlin/internal") &&
|
||||
name == "apiVersionIsAtLeast" &&
|
||||
desc == "(III)Z"
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.getIntConstValue(): Int? =
|
||||
when (this) {
|
||||
is InsnNode ->
|
||||
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
|
||||
opcode - Opcodes.ICONST_0
|
||||
else
|
||||
null
|
||||
|
||||
is IntInsnNode ->
|
||||
when (opcode) {
|
||||
Opcodes.BIPUSH -> operand
|
||||
Opcodes.SIPUSH -> operand
|
||||
else -> null
|
||||
}
|
||||
|
||||
is LdcInsnNode -> cst as? Int
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// FILE: 1.kt
|
||||
package kotlin.internal
|
||||
|
||||
fun apiVersionIsAtLeast(epic: Int, major: Int, minor: Int): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
var properFunctionWasClled = false
|
||||
|
||||
fun doSomethingNew() {
|
||||
properFunctionWasClled = true
|
||||
}
|
||||
|
||||
fun doSomethingOld() {
|
||||
throw AssertionError("Should not be called")
|
||||
}
|
||||
|
||||
inline fun versionDependentInlineFun() {
|
||||
if (apiVersionIsAtLeast(1, 1, 0)) {
|
||||
doSomethingNew()
|
||||
}
|
||||
else {
|
||||
doSomethingOld()
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
versionDependentInlineFun()
|
||||
}
|
||||
|
||||
|
||||
// FILE: 2.kt
|
||||
import kotlin.internal.*
|
||||
|
||||
fun box(): String {
|
||||
test()
|
||||
if (!properFunctionWasClled) return "Fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+15
@@ -642,6 +642,21 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class BytecodePreprocessing extends AbstractIrBlackBoxInlineCodegenTest {
|
||||
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("apiVersionAtLeast1.kt")
|
||||
public void testApiVersionAtLeast1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+15
@@ -642,6 +642,21 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class BytecodePreprocessing extends AbstractIrCompileKotlinAgainstInlineKotlinTest {
|
||||
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("apiVersionAtLeast1.kt")
|
||||
public void testApiVersionAtLeast1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -642,6 +642,21 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class BytecodePreprocessing extends AbstractBlackBoxInlineCodegenTest {
|
||||
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("apiVersionAtLeast1.kt")
|
||||
public void testApiVersionAtLeast1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+15
@@ -642,6 +642,21 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class BytecodePreprocessing extends AbstractCompileKotlinAgainstInlineKotlinTest {
|
||||
public void testAllFilesPresentInBytecodePreprocessing() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/bytecodePreprocessing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("apiVersionAtLeast1.kt")
|
||||
public void testApiVersionAtLeast1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/bytecodePreprocessing/apiVersionAtLeast1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/callableReference")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user