JVM: move synthetic line number generation to the inliner
This makes the behavior of JVM and JVM_IR the same without having to deal with PSI nodes.
This commit is contained in:
@@ -78,7 +78,6 @@ import static org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.isAccessor;
|
|||||||
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
||||||
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION;
|
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION;
|
||||||
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
|
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
|
||||||
import static org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER;
|
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
|
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
||||||
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
|
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
|
||||||
@@ -592,7 +591,6 @@ public class FunctionCodegen {
|
|||||||
Label methodEnd;
|
Label methodEnd;
|
||||||
|
|
||||||
int functionFakeIndex = -1;
|
int functionFakeIndex = -1;
|
||||||
int lambdaFakeIndex = -1;
|
|
||||||
|
|
||||||
if (context.getParentContext() instanceof MultifileClassFacadeContext) {
|
if (context.getParentContext() instanceof MultifileClassFacadeContext) {
|
||||||
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
|
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
|
||||||
@@ -623,20 +621,8 @@ public class FunctionCodegen {
|
|||||||
ValueParameterDescriptor continuationValueDescriptor = view.getValueParameters().get(view.getValueParameters().size() - 1);
|
ValueParameterDescriptor continuationValueDescriptor = view.getValueParameters().get(view.getValueParameters().size() - 1);
|
||||||
frameMap.enter(continuationValueDescriptor, typeMapper.mapType(continuationValueDescriptor));
|
frameMap.enter(continuationValueDescriptor, typeMapper.mapType(continuationValueDescriptor));
|
||||||
}
|
}
|
||||||
if (context.isInlineMethodContext()) {
|
if (context.isInlineMethodContext() || context instanceof InlineLambdaContext) {
|
||||||
functionFakeIndex = newFakeTempIndex(mv, frameMap, -1);
|
functionFakeIndex = newFakeTempIndex(mv, frameMap);
|
||||||
}
|
|
||||||
|
|
||||||
if (context instanceof InlineLambdaContext) {
|
|
||||||
int lineNumber = -1;
|
|
||||||
if (strategy instanceof ClosureGenerationStrategy) {
|
|
||||||
KtDeclarationWithBody declaration = ((ClosureGenerationStrategy) strategy).getDeclaration();
|
|
||||||
BindingContext bindingContext = typeMapper.getBindingContext();
|
|
||||||
if (declaration instanceof KtFunctionLiteral && isLambdaPassedToInlineOnly((KtFunction) declaration, bindingContext)) {
|
|
||||||
lineNumber = LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lambdaFakeIndex = newFakeTempIndex(mv, frameMap, lineNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
methodEntry = new Label();
|
methodEntry = new Label();
|
||||||
@@ -665,7 +651,7 @@ public class FunctionCodegen {
|
|||||||
|
|
||||||
generateLocalVariableTable(
|
generateLocalVariableTable(
|
||||||
mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), parentCodegen.state,
|
mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), parentCodegen.state,
|
||||||
(functionFakeIndex >= 0 ? 1 : 0) + (lambdaFakeIndex >= 0 ? 1 : 0)
|
(functionFakeIndex >= 0 ? 1 : 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: it's best to move all below logic to 'generateLocalVariableTable' method
|
//TODO: it's best to move all below logic to 'generateLocalVariableTable' method
|
||||||
@@ -676,9 +662,7 @@ public class FunctionCodegen {
|
|||||||
Type.INT_TYPE.getDescriptor(), null,
|
Type.INT_TYPE.getDescriptor(), null,
|
||||||
methodEntry, methodEnd,
|
methodEntry, methodEnd,
|
||||||
functionFakeIndex);
|
functionFakeIndex);
|
||||||
}
|
} else if (context instanceof InlineLambdaContext && thisType != null && functionFakeIndex != -1) {
|
||||||
|
|
||||||
if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) {
|
|
||||||
String internalName = thisType.getInternalName();
|
String internalName = thisType.getInternalName();
|
||||||
String lambdaLocalName = StringsKt.substringAfterLast(internalName, '/', internalName);
|
String lambdaLocalName = StringsKt.substringAfterLast(internalName, '/', internalName);
|
||||||
|
|
||||||
@@ -696,7 +680,7 @@ public class FunctionCodegen {
|
|||||||
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + "-" + functionName + "-" + lambdaLocalName,
|
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + "-" + functionName + "-" + lambdaLocalName,
|
||||||
Type.INT_TYPE.getDescriptor(), null,
|
Type.INT_TYPE.getDescriptor(), null,
|
||||||
methodEntry, methodEnd,
|
methodEntry, methodEnd,
|
||||||
lambdaFakeIndex);
|
functionFakeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,13 +698,8 @@ public class FunctionCodegen {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int newFakeTempIndex(@NotNull MethodVisitor mv, @NotNull FrameMap frameMap, int lineNumber) {
|
private static int newFakeTempIndex(@NotNull MethodVisitor mv, @NotNull FrameMap frameMap) {
|
||||||
int fakeIndex = frameMap.enterTemp(Type.INT_TYPE);
|
int fakeIndex = frameMap.enterTemp(Type.INT_TYPE);
|
||||||
if (lineNumber >= 0) {
|
|
||||||
Label label = new Label();
|
|
||||||
mv.visitLabel(label);
|
|
||||||
mv.visitLineNumber(lineNumber, label);
|
|
||||||
}
|
|
||||||
mv.visitLdcInsn(0);
|
mv.visitLdcInsn(0);
|
||||||
mv.visitVarInsn(ISTORE, fakeIndex);
|
mv.visitVarInsn(ISTORE, fakeIndex);
|
||||||
return fakeIndex;
|
return fakeIndex;
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ class MethodInliner(
|
|||||||
visitInsn(Opcodes.NOP)
|
visitInsn(Opcodes.NOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info)
|
||||||
addInlineMarker(this, true)
|
addInlineMarker(this, true)
|
||||||
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
||||||
|
|
||||||
@@ -306,7 +307,7 @@ class MethodInliner(
|
|||||||
.put(OBJECT_TYPE, erasedInvokeFunction.returnType, this)
|
.put(OBJECT_TYPE, erasedInvokeFunction.returnType, this)
|
||||||
setLambdaInlining(false)
|
setLambdaInlining(false)
|
||||||
addInlineMarker(this, false)
|
addInlineMarker(this, false)
|
||||||
inlineOnlySmapSkipper?.markCallSiteLineNumber(remappingMethodAdapter)
|
inlineOnlySmapSkipper?.onInlineLambdaEnd(remappingMethodAdapter)
|
||||||
} else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
} else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
||||||
//TODO add proper message
|
//TODO add proper message
|
||||||
var info = transformationInfo as? AnonymousObjectTransformationInfo ?: throw AssertionError(
|
var info = transformationInfo as? AnonymousObjectTransformationInfo ?: throw AssertionError(
|
||||||
|
|||||||
@@ -536,10 +536,24 @@ fun isFakeLocalVariableForInline(name: String): Boolean {
|
|||||||
internal fun isThis0(name: String): Boolean = AsmUtil.CAPTURED_THIS_FIELD == name
|
internal fun isThis0(name: String): Boolean = AsmUtil.CAPTURED_THIS_FIELD == name
|
||||||
|
|
||||||
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||||
|
|
||||||
private val callLineNumber = codegen.lastLineNumber
|
private val callLineNumber = codegen.lastLineNumber
|
||||||
|
|
||||||
fun markCallSiteLineNumber(mv: MethodVisitor) {
|
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo) {
|
||||||
|
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
||||||
|
if (callLineNumber >= 0 && firstLine == callLineNumber) {
|
||||||
|
// We want the debugger to be able to break both on the inline call itself, plus on each
|
||||||
|
// invocation of the inline lambda passed to it. For that to happen there needs to be at least
|
||||||
|
// one different line number in between those breakpoints for the VM to emit a locatable event.
|
||||||
|
// @InlineOnly functions, however, contain no line numbers, so if the lambda is single-line,
|
||||||
|
// the entire call will "meld" into a single region. To break it up, we insert a different line
|
||||||
|
// number that is remapped by the SMAP to a line that does not exist.
|
||||||
|
val label = Label()
|
||||||
|
mv.visitLabel(label)
|
||||||
|
mv.visitLineNumber(JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER, label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onInlineLambdaEnd(mv: MethodVisitor) {
|
||||||
if (callLineNumber >= 0) {
|
if (callLineNumber >= 0) {
|
||||||
val label = Label()
|
val label = Label()
|
||||||
mv.visitLabel(label)
|
mv.visitLabel(label)
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
inline fun <T, R> T.myLet(block: (T) -> R) = block(this)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val k = "".myLet {
|
||||||
|
it + "K"
|
||||||
|
}
|
||||||
|
return "O".myLet(fun (it: String): String {
|
||||||
|
return it + k
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation.
|
||||||
|
// 0 LINENUMBER 65100
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
inline fun <T, R> T.myLet(block: (T) -> R) = block(this)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val k = "".myLet { it + "K" }
|
||||||
|
return "O".myLet(fun (it: String): String { return it + k })
|
||||||
|
}
|
||||||
|
|
||||||
|
// See KT-23064 for the problem and InlineOnlySmapSkipper for an explanation.
|
||||||
|
// 2 LINENUMBER 65100
|
||||||
@@ -3386,6 +3386,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stdlibInlineOnly.kt")
|
||||||
|
public void testStdlibInlineOnly() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stdlibInlineOnlyOneLine.kt")
|
||||||
|
public void testStdlibInlineOnlyOneLine() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("tryCatch.kt")
|
@TestMetadata("tryCatch.kt")
|
||||||
public void testTryCatch() throws Exception {
|
public void testTryCatch() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
||||||
|
|||||||
+10
@@ -3431,6 +3431,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/singleThen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stdlibInlineOnly.kt")
|
||||||
|
public void testStdlibInlineOnly() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnly.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("stdlibInlineOnlyOneLine.kt")
|
||||||
|
public void testStdlibInlineOnlyOneLine() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/stdlibInlineOnlyOneLine.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("tryCatch.kt")
|
@TestMetadata("tryCatch.kt")
|
||||||
public void testTryCatch() throws Exception {
|
public void testTryCatch() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user