Primitive support for LocalVariables for function parameters

This commit is contained in:
Denis Vnukov
2018-08-07 08:25:57 -07:00
committed by Mikhael Bogdanov
parent 1a56af9bb0
commit 65c79ecfe9
21 changed files with 293 additions and 26 deletions
@@ -58,7 +58,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.utils.StringsKt;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
@@ -849,12 +848,12 @@ public class FunctionCodegen {
if (kind == JvmMethodParameterKind.VALUE) {
ValueParameterDescriptor parameter = valueParameterIterator.next();
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
String nameForDestructuredParameter = ValueParameterDescriptorImpl.getNameForDestructuredParameterOrNull(parameter);
parameterName =
destructuringVariables == null
nameForDestructuredParameter == null
? computeParameterName(i, parameter)
: "$" + joinParameterNames(destructuringVariables);
: nameForDestructuredParameter;
}
else {
String lowercaseKind = kind.name().toLowerCase();
@@ -904,11 +903,11 @@ public class FunctionCodegen {
List<ValueParameterDescriptor> destructuredParametersForSuspendLambda
) {
for (ValueParameterDescriptor parameter : destructuredParametersForSuspendLambda) {
List<VariableDescriptor> destructuringVariables = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(parameter);
if (destructuringVariables == null) continue;
String nameForDestructuredParameter = ValueParameterDescriptorImpl.getNameForDestructuredParameterOrNull(parameter);
if (nameForDestructuredParameter == null) continue;
Type type = typeMapper.mapType(parameter.getType());
mv.visitLocalVariable("$" + joinParameterNames(destructuringVariables), type.getDescriptor(), null, methodBegin, methodEnd, shift);
mv.visitLocalVariable(nameForDestructuredParameter, type.getDescriptor(), null, methodBegin, methodEnd, shift);
shift += type.getSize();
}
return shift;
@@ -923,14 +922,6 @@ public class FunctionCodegen {
return parameter.getName().asString();
}
private static String joinParameterNames(@NotNull List<VariableDescriptor> variables) {
// stub for anonymous destructuring declaration entry
return StringsKt.join(
CollectionsKt.map(variables, descriptor -> descriptor.getName().isSpecial() ? "$_$" : descriptor.getName().asString()),
"_"
);
}
private static void generateFacadeDelegateMethodBody(
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@@ -24,11 +24,9 @@ import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeAlias
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.toKotlinType
@@ -118,6 +116,7 @@ class ExpressionCodegen(
fun generate() {
mv.visitCode()
val startLabel = markNewLabel()
irFunction.markLineNumber(true)
val info = BlockInfo.create()
val result = irFunction.body!!.accept(this, info)
@@ -137,9 +136,35 @@ class ExpressionCodegen(
}
}
writeLocalVariablesInTable(info)
writeParameterInLocalVariableTable(startLabel)
mv.visitEnd()
}
private fun writeParameterInLocalVariableTable(startLabel: Label) {
if (!irFunction.isStatic) {
mv.visitLocalVariable("this", classCodegen.type.descriptor, null, startLabel, markNewLabel(), 0)
}
val extensionReceiverParameter = irFunction.extensionReceiverParameter
if (extensionReceiverParameter != null) {
writeValueParameterInLocalVariableTable(extensionReceiverParameter, startLabel)
}
for (param in irFunction.valueParameters) {
writeValueParameterInLocalVariableTable(param, startLabel)
}
}
private fun writeValueParameterInLocalVariableTable(param: IrValueParameter, startLabel: Label) {
val descriptor = param.descriptor
val nameForDestructuredParameter = if (descriptor is ValueParameterDescriptor)
ValueParameterDescriptorImpl.getNameForDestructuredParameterOrNull(descriptor) else null
val type = typeMapper.mapType(descriptor)
// NOTE: we expect all value parameters to be present in the frame.
mv.visitLocalVariable(
nameForDestructuredParameter ?: param.name.asString(),
type.descriptor, null, startLabel, markNewLabel(), findLocalIndex(param.symbol)
)
}
private fun endsWithReturn(body: IrBody): Boolean {
val lastStatement = if (body is IrStatementContainer) {
body.statements.lastOrNull() ?: body
@@ -169,7 +194,18 @@ class ExpressionCodegen(
private fun writeLocalVariablesInTable(info: BlockInfo) {
val endLabel = markNewLabel()
info.variables.forEach {
mv.visitLocalVariable(it.declaration.name.asString(), it.type.descriptor, null, it.startLabel, endLabel, it.index)
when (it.declaration.origin) {
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
IrDeclarationOrigin.FOR_LOOP_ITERATOR,
IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE -> {
// Ignore implicitly created variables
}
else -> {
mv.visitLocalVariable(
it.declaration.name.asString(), it.type.descriptor, null, it.startLabel, endLabel, it.index
)
}
}
}
info.variables.reversed().forEach {
@@ -1141,4 +1177,4 @@ fun DefaultCallArgs.generateOnStackIfNeeded(callGenerator: IrCallGenerator, isCo
internal fun CallableDescriptor.isInlineCall(state: GenerationState) =
(!state.isInlineDisabled || InlineUtil.containsReifiedTypeParameters(this)) &&
(InlineUtil.isInline(this) || InlineUtil.isArrayConstructorWithLambda(this))
(InlineUtil.isInline(this) || InlineUtil.isArrayConstructorWithLambda(this))
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class A(val x: String, val y: Int)
fun foo(a: A, block: (A) -> String): String = block(a)
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class Data(val x: String, val y: Int)
suspend fun test() {
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
class A<T>(val x: String, val y: String, val z: T)
suspend fun <T> foo(a: A<T>, block: suspend (A<T>) -> String): String = block(a)
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class A<T, F>(val x: T, val y: F)
suspend fun <X, Y> foo(a: A<X, Y>, block: suspend (A<X, Y>) -> String) = block(a)
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class A(val x: String, val y: String)
suspend inline fun foo(a: A, block: suspend (A) -> String): String = block(a)
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class A(val x: String, val y: String)
suspend fun foo(a: A, block: suspend (Int, A, String) -> String): String = block(1, a, "#")
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
class A {
operator fun component1() = "O"
operator fun component2(): String = throw RuntimeException("fail 0")
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
public fun <T> Iterable<T>.myforEach(operation: (T) -> Unit) : Unit {
for (element in this) operation(element)
}
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
fun foo() {
fun bar() : (Int) -> Unit {
return {
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
class C {
@kotlin.jvm.JvmOverloads fun foo(firstParam: Int, secondParam: String = "") {
}
+1
View File
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
class A(val value: String)
fun A.test(): String {
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
fun foo() {
var a = {
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
fun foo() {
fun bar() {
}
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
data class A(val x: Double = 1.0, val y: String = "", val z: Char = '0')
fun foo(a: A, block: (A, String, Int) -> String): String = block(a, "", 1)
@@ -42,8 +42,8 @@ import java.util.regex.Pattern;
*/
public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithTmpdir {
private File ktFile;
private KotlinCoreEnvironment environment;
protected File ktFile;
protected KotlinCoreEnvironment environment;
public AbstractCheckLocalVariablesTableTest() {
}
@@ -82,6 +82,10 @@ public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithT
ClassReader cr = new ClassReader(outputFile.asByteArray());
List<LocalVariable> actualLocalVariables = readLocalVariable(cr, methodName);
doCompare(text, actualLocalVariables);
}
protected void doCompare(String text, List<LocalVariable> actualLocalVariables) {
KotlinTestUtils.assertEqualsToFile(ktFile, text.substring(0, text.indexOf("// VARIABLE : ")) + getActualVariablesAsString(actualLocalVariables));
}
@@ -93,8 +97,7 @@ public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithT
return builder.toString();
}
private static class LocalVariable {
protected static class LocalVariable {
private final String name;
private final String type;
private final int index;
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.ir
import com.intellij.openapi.util.Comparing
import org.jetbrains.kotlin.codegen.AbstractCheckLocalVariablesTableTest
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.junit.ComparisonFailure
import java.nio.charset.Charset
abstract class AbstractIrCheckLocalVariablesTableTest : AbstractCheckLocalVariablesTableTest() {
@Throws(Exception::class)
override fun setUp() {
super.setUp()
assert(environment != null)
environment.configuration.put(JVMConfigurationKeys.IR, true)
}
override fun doCompare(text: String?, actualLocalVariables: MutableList<LocalVariable>) {
val actual = getActualVariablesAsList(actualLocalVariables)
val expected = getExpectedVariablesAsList()
if (!Comparing.equal(expected, actual)) {
throw ComparisonFailure(
"Variables differ from expected",
expected.joinToString("\n"),
actual.joinToString("\n")
)
}
}
private fun getActualVariablesAsList(list: List<LocalVariable>): List<String> {
return list.map { it.toString() }
.map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } // Ignore index
.sorted()
}
private fun getExpectedVariablesAsList(): List<String> {
return ktFile.readLines(Charset.forName("utf-8"))
.filter { line -> line.startsWith("// VARIABLE ") }
.filter { !it.contains("NAME=\$i\$") }
.map { line -> line.replaceFirst("INDEX=\\d+".toRegex(), "INDEX=*") } // Ignore index
.sorted()
}
}
@@ -0,0 +1,154 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.ir;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/checkLocalVariablesTable")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class IrCheckLocalVariablesTableTestGenerated extends AbstractIrCheckLocalVariablesTableTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInCheckLocalVariablesTable() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/checkLocalVariablesTable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("catchClause.kt")
public void testCatchClause() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/catchClause.kt");
}
@TestMetadata("copyFunction.kt")
public void testCopyFunction() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/copyFunction.kt");
}
@TestMetadata("destructuringInLambdas.kt")
public void testDestructuringInLambdas() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInLambdas.kt");
}
@TestMetadata("destructuringInlineLambda.kt")
public void testDestructuringInlineLambda() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInlineLambda.kt");
}
@TestMetadata("inlineLambdaWithItParam.kt")
public void testInlineLambdaWithItParam() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt");
}
@TestMetadata("inlineLambdaWithParam.kt")
public void testInlineLambdaWithParam() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt");
}
@TestMetadata("inlineProperty.kt")
public void testInlineProperty() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/inlineProperty.kt");
}
@TestMetadata("inlineSimple.kt")
public void testInlineSimple() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/inlineSimple.kt");
}
@TestMetadata("inlineSimpleChain.kt")
public void testInlineSimpleChain() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt");
}
@TestMetadata("itInLambda.kt")
public void testItInLambda() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/itInLambda.kt");
}
@TestMetadata("itInReturnedLambda.kt")
public void testItInReturnedLambda() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/itInReturnedLambda.kt");
}
@TestMetadata("jvmOverloads.kt")
public void testJvmOverloads() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/jvmOverloads.kt");
}
@TestMetadata("kt11117.kt")
public void testKt11117() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/kt11117.kt");
}
@TestMetadata("lambdaAsVar.kt")
public void testLambdaAsVar() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/lambdaAsVar.kt");
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/localFun.kt");
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
}
@TestMetadata("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DestructuringInSuspendLambda extends AbstractIrCheckLocalVariablesTableTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInDestructuringInSuspendLambda() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
}
@TestMetadata("dataClass.kt")
public void testDataClass() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/dataClass.kt");
}
@TestMetadata("extensionComponents.kt")
public void testExtensionComponents() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/extensionComponents.kt");
}
@TestMetadata("generic.kt")
public void testGeneric() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/generic.kt");
}
@TestMetadata("inline.kt")
public void testInline() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/inline.kt");
}
@TestMetadata("otherParameters.kt")
public void testOtherParameters() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/otherParameters.kt");
}
@TestMetadata("underscoreNames.kt")
public void testUnderscoreNames() throws Exception {
runTest("compiler/testData/checkLocalVariablesTable/destructuringInSuspendLambda/underscoreNames.kt");
}
}
}
@@ -27,10 +27,10 @@ import org.jetbrains.kotlin.cli.AbstractCliTest
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.defaultConstructor.AbstractDefaultArgumentsReflectionTest
import org.jetbrains.kotlin.codegen.flags.AbstractWriteFlagsTest
import org.jetbrains.kotlin.codegen.AbstractCustomScriptCodegenTest
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxAgainstJavaCodegenTest
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxCodegenTest
import org.jetbrains.kotlin.codegen.ir.AbstractIrBlackBoxInlineCodegenTest
import org.jetbrains.kotlin.codegen.ir.AbstractIrCheckLocalVariablesTableTest
import org.jetbrains.kotlin.generators.tests.generator.testGroup
import org.jetbrains.kotlin.generators.util.KT_OR_KTS_WITHOUT_DOTS_IN_NAME
import org.jetbrains.kotlin.integration.AbstractAntTaskTest
@@ -356,6 +356,10 @@ fun main(args: Array<String>) {
model("codegen/boxAgainstJava", targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractIrCheckLocalVariablesTableTest> {
model("checkLocalVariablesTable", targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractIrBlackBoxInlineCodegenTest> {
model("codegen/boxInline", targetBackend = TargetBackend.JVM_IR)
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.utils.join
open class ValueParameterDescriptorImpl(
containingDeclaration: CallableDescriptor,
@@ -60,6 +61,16 @@ open class ValueParameterDescriptorImpl(
WithDestructuringDeclaration(containingDeclaration, original, index, annotations, name, outType,
declaresDefaultValue, isCrossinline, isNoinline, varargElementType, source,
destructuringVariables)
@JvmStatic
fun getNameForDestructuredParameterOrNull(valueParameterDescriptor: ValueParameterDescriptor): String? {
val variablesOrNull = ValueParameterDescriptorImpl.getDestructuringVariablesOrNull(valueParameterDescriptor)
return if (variablesOrNull == null) null else
"$" + join(
variablesOrNull.map { descriptor -> if (descriptor.name.isSpecial) "\$_\$" else descriptor.name.asString() },
"_"
)
}
}
class WithDestructuringDeclaration internal constructor(
@@ -81,6 +92,13 @@ open class ValueParameterDescriptorImpl(
// as value parameters.
// Must be forced via ForceResolveUtil.forceResolveAllContents()
val destructuringVariables by lazy(destructuringVariables)
override fun copy(newOwner: CallableDescriptor, newName: Name, newIndex: Int): ValueParameterDescriptor {
return WithDestructuringDeclaration(
newOwner, null, newIndex, annotations, newName, type, declaresDefaultValue(),
isCrossinline, isNoinline, varargElementType, SourceElement.NO_SOURCE
) { destructuringVariables }
}
}
private val original: ValueParameterDescriptor = original ?: this