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:
pyos
2020-04-23 14:34:13 +02:00
committed by max-kammerer
parent 91b6daeacc
commit deecb6a28e
7 changed files with 70 additions and 30 deletions
@@ -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.DELEGATION;
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.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
@@ -592,7 +591,6 @@ public class FunctionCodegen {
Label methodEnd;
int functionFakeIndex = -1;
int lambdaFakeIndex = -1;
if (context.getParentContext() instanceof MultifileClassFacadeContext) {
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
@@ -623,20 +621,8 @@ public class FunctionCodegen {
ValueParameterDescriptor continuationValueDescriptor = view.getValueParameters().get(view.getValueParameters().size() - 1);
frameMap.enter(continuationValueDescriptor, typeMapper.mapType(continuationValueDescriptor));
}
if (context.isInlineMethodContext()) {
functionFakeIndex = newFakeTempIndex(mv, frameMap, -1);
}
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);
if (context.isInlineMethodContext() || context instanceof InlineLambdaContext) {
functionFakeIndex = newFakeTempIndex(mv, frameMap);
}
methodEntry = new Label();
@@ -665,7 +651,7 @@ public class FunctionCodegen {
generateLocalVariableTable(
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
@@ -676,9 +662,7 @@ public class FunctionCodegen {
Type.INT_TYPE.getDescriptor(), null,
methodEntry, methodEnd,
functionFakeIndex);
}
if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) {
} else if (context instanceof InlineLambdaContext && thisType != null && functionFakeIndex != -1) {
String internalName = thisType.getInternalName();
String lambdaLocalName = StringsKt.substringAfterLast(internalName, '/', internalName);
@@ -696,7 +680,7 @@ public class FunctionCodegen {
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + "-" + functionName + "-" + lambdaLocalName,
Type.INT_TYPE.getDescriptor(), null,
methodEntry, methodEnd,
lambdaFakeIndex);
functionFakeIndex);
}
}
@@ -714,13 +698,8 @@ public class FunctionCodegen {
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);
if (lineNumber >= 0) {
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(lineNumber, label);
}
mv.visitLdcInsn(0);
mv.visitVarInsn(ISTORE, fakeIndex);
return fakeIndex;
@@ -270,6 +270,7 @@ class MethodInliner(
visitInsn(Opcodes.NOP)
}
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info)
addInlineMarker(this, true)
val lambdaParameters = info.addAllParameters(nodeRemapper)
@@ -306,7 +307,7 @@ class MethodInliner(
.put(OBJECT_TYPE, erasedInvokeFunction.returnType, this)
setLambdaInlining(false)
addInlineMarker(this, false)
inlineOnlySmapSkipper?.markCallSiteLineNumber(remappingMethodAdapter)
inlineOnlySmapSkipper?.onInlineLambdaEnd(remappingMethodAdapter)
} else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
//TODO add proper message
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
class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
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) {
val label = 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
@@ -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");
}
@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")
public void testTryCatch() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");
@@ -3431,6 +3431,16 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
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")
public void testTryCatch() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/lineNumbers/tryCatch.kt");