Add default lambda parsing

This commit is contained in:
Mikhael Bogdanov
2017-05-10 13:02:58 +02:00
parent 70e550e984
commit 980414674a
4 changed files with 188 additions and 67 deletions
@@ -87,7 +87,7 @@ public class InlineCodegen extends CallGenerator {
private final boolean isSameModule;
private final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder();
private final Map<Integer, LambdaInfo> expressionMap = new HashMap<>();
private final Map<Integer, LambdaInfo> expressionMap = new LinkedHashMap<>();
private final ReifiedTypeInliner reifiedTypeInliner;
@@ -413,7 +413,10 @@ public class InlineCodegen extends CallGenerator {
defaultSourceMapper.setCallSiteMarker(new CallSiteMarker(codegen.getLastLineNumber()));
MethodNode node = nodeAndSmap.getNode();
if (callDefault) {
MethodInlinerUtilKt.expandMaskConditionsAndUpdateVariableNodes(node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex);
List<DefaultLambda> defaultLambdas = DefaultMethodUtilKt.expandMaskConditionsAndUpdateVariableNodes(
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex,
DefaultMethodUtilKt.extractDefaultLambdaOffsetAndDescriptor(jvmSignature, functionDescriptor)
);
}
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node);
generateClosuresBodies();
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
@@ -71,7 +72,13 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean, @JvmField val is
}
class DefaultLambda(override val lambdaClassType: Type, lambdaArgs: Array<Type>) : LambdaInfo(false, false) {
class DefaultLambda(
override val lambdaClassType: Type,
val capturedArgs: Array<Type>,
val parameterDescriptor: ValueParameterDescriptor,
val initInstuctions: List<AbstractInsnNode>,
val offset: Int
) : LambdaInfo(false, false) {
override val invokeMethod: Method
get() = TODO("not implemented")
@@ -16,12 +16,13 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
@@ -89,67 +90,14 @@ private fun MethodInliner.getLambdaIfExistsAndMarkInstructions(
fun SourceValue.singleOrNullInsn() = insns.singleOrNull()
fun expandMaskConditionsAndUpdateVariableNodes(node: MethodNode, maskStartIndex: Int, masks: List<Int>, methodHandlerIndex: Int) {
class Condition(mask: Int, constant: Int, val maskInstruction: VarInsnNode, val jumpInstruction: JumpInsnNode, val varIndex: Int) {
val expandNotDelete = mask and constant != 0
}
fun isMaskIndex(varIndex: Int): Boolean {
return maskStartIndex <= varIndex && varIndex < maskStartIndex + masks.size
}
val maskProcessingHeader = node.instructions.asSequence().takeWhile {
if (it is VarInsnNode) {
if (isMaskIndex(it.`var`)) {
/*if slot for default mask is updated than we occurred in actual function body*/
return@takeWhile it.opcode == Opcodes.ILOAD
}
else if (isMethodHandleIndex(methodHandlerIndex, it)) {
return@takeWhile it.opcode == Opcodes.ALOAD
}
fun parameterOffsets(valueParameters: List<JvmMethodParameterSignature>): Array<Int> {
var offset = 0
return Array(valueParameters.size) { index ->
if (index == 0) {
0
}
true
}
val conditions = maskProcessingHeader.filterIsInstance<VarInsnNode>().mapNotNull {
if (isMaskIndex(it.`var`) &&
it.next?.next?.opcode == Opcodes.IAND &&
it.next.next.next?.opcode == Opcodes.IFEQ) {
val jumpInstruction = it.next?.next?.next as JumpInsnNode
Condition(
masks[it.`var` - maskStartIndex],
InlineCodegenUtil.getConstant(it.next),
it,
jumpInstruction,
(jumpInstruction.label.previous as VarInsnNode).`var`
)
}
else if (isMethodHandleIndex(methodHandlerIndex, it) &&
it.next?.opcode == Opcodes.IFNULL &&
it.next.next?.opcode == Opcodes.NEW) {
//Always delete method handle for now
//This logic should be updated when method handles would be supported
Condition(0, 0, it,it.next as JumpInsnNode, -1)
}
else null
}
val indexToVarNode = node.localVariables?.filter { it.index < maskStartIndex }?.associateBy { it.index } ?: emptyMap()
val toDelete = arrayListOf<AbstractInsnNode>()
conditions.forEach {
val jumpInstruction = it.jumpInstruction
InsnSequence(it.maskInstruction, (if (it.expandNotDelete) jumpInstruction.next else jumpInstruction.label)).forEach {
toDelete.add(it)
}
if (it.expandNotDelete) {
indexToVarNode[it.varIndex]?.let { varNode ->
varNode.start = it.jumpInstruction.label
}
else offset.apply {
offset += valueParameters[index - 1].asmType.size
}
}
toDelete.forEach {
node.instructions.remove(it)
}
}
private fun isMethodHandleIndex(methodHandlerIndex: Int, it: VarInsnNode) = methodHandlerIndex == it.`var`
}
@@ -0,0 +1,163 @@
/*
* 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.inline
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
private data class Condition(
val mask: Int, val constant: Int,
val maskInstruction: VarInsnNode,
val jumpInstruction: JumpInsnNode,
val varInsNode: VarInsnNode?
) {
val expandNotDelete = mask and constant != 0
val varIndex = varInsNode?.`var` ?: 0
}
fun extractDefaultLambdaOffsetAndDescriptor(jvmSignature: JvmMethodSignature, functionDescriptor: FunctionDescriptor): Map<Int, ValueParameterDescriptor> {
val valueParameters = jvmSignature.valueParameters
val parameterOffsets = parameterOffsets(valueParameters)
val valueParameterOffset = valueParameters.takeWhile { it.kind != JvmMethodParameterKind.VALUE }.size
return functionDescriptor.valueParameters.filter {
InlineUtil.isInlineLambdaParameter(it) && it.declaresDefaultValue()
}.associateBy {
parameterOffsets[valueParameterOffset + it.index]
}
}
fun expandMaskConditionsAndUpdateVariableNodes(
node: MethodNode,
maskStartIndex: Int,
masks: List<Int>,
methodHandlerIndex: Int,
defaultLambdas: Map<Int, ValueParameterDescriptor>
): List<DefaultLambda> {
fun isMaskIndex(varIndex: Int): Boolean {
return maskStartIndex <= varIndex && varIndex < maskStartIndex + masks.size
}
val maskProcessingHeader = node.instructions.asSequence().takeWhile {
if (it is VarInsnNode) {
if (isMaskIndex(it.`var`)) {
/*if slot for default mask is updated than we occurred in actual function body*/
return@takeWhile it.opcode == Opcodes.ILOAD
}
else if (methodHandlerIndex == it.`var`) {
return@takeWhile it.opcode == Opcodes.ALOAD
}
}
true
}
val conditions = maskProcessingHeader.filterIsInstance<VarInsnNode>().mapNotNull {
if (isMaskIndex(it.`var`) &&
it.next?.next?.opcode == Opcodes.IAND &&
it.next.next.next?.opcode == Opcodes.IFEQ) {
val jumpInstruction = it.next?.next?.next as JumpInsnNode
Condition(
masks[it.`var` - maskStartIndex],
InlineCodegenUtil.getConstant(it.next),
it,
jumpInstruction,
jumpInstruction.label.previous as VarInsnNode
)
}
else if (methodHandlerIndex == it.`var` &&
it.next?.opcode == Opcodes.IFNULL &&
it.next.next?.opcode == Opcodes.NEW) {
//Always delete method handle for now
//This logic should be updated when method handles would be supported
Condition(0, 0, it, it.next as JumpInsnNode, null)
}
else null
}.toList()
val defaultLambdasInfo = extractDefaultLambdasInfo(conditions, defaultLambdas)
val indexToVarNode = node.localVariables?.filter { it.index < maskStartIndex }?.associateBy { it.index } ?: emptyMap()
val toDelete = arrayListOf<AbstractInsnNode>()
conditions.forEach {
val jumpInstruction = it.jumpInstruction
InsnSequence(it.maskInstruction, (if (it.expandNotDelete) jumpInstruction.next else jumpInstruction.label)).forEach {
toDelete.add(it)
}
if (it.expandNotDelete) {
indexToVarNode[it.varIndex]?.let { varNode ->
varNode.start = it.jumpInstruction.label
}
}
}
toDelete.forEach {
node.instructions.remove(it)
}
return defaultLambdasInfo
}
private fun extractDefaultLambdasInfo(conditions: List<Condition>, defaultLambdas: Map<Int, ValueParameterDescriptor>): List<DefaultLambda> {
val defaultLambdaConditions = conditions.filter { it.expandNotDelete && defaultLambdas.contains(it.varIndex) }
return defaultLambdaConditions.map {
val varAssignmentInstruction = it.varInsNode!!
var instanceInstuction = varAssignmentInstruction.previous
if (instanceInstuction is TypeInsnNode && instanceInstuction.opcode == Opcodes.CHECKCAST) {
instanceInstuction = instanceInstuction.previous
}
when (instanceInstuction) {
is MethodInsnNode -> {
assert(instanceInstuction.name == "<init>") { "Expected constructor call for default lambda, but $instanceInstuction" }
val ownerInternalName = instanceInstuction.owner
val instanceCreation = InsnSequence(it.jumpInstruction, it.jumpInstruction.label).filter {
it.opcode == Opcodes.NEW && (it as TypeInsnNode).desc == ownerInternalName
}.single()
assert(instanceCreation.next?.opcode == Opcodes.DUP) {
"Dup should follow default lambda instanceInstuction creation but ${instanceCreation.next}"
}
DefaultLambda(
Type.getObjectType(instanceInstuction.owner),
Type.getArgumentTypes(instanceInstuction.desc),
defaultLambdas[it.varIndex]!!,
listOf(instanceCreation, instanceCreation.next) + InsnSequence(instanceInstuction, varAssignmentInstruction).toList(),
it.varIndex
)
}
is FieldInsnNode -> DefaultLambda(
Type.getObjectType(instanceInstuction.owner),
emptyArray<Type>(),
defaultLambdas[it.varIndex]!!,
InsnSequence(instanceInstuction, varAssignmentInstruction).toList(),
it.varIndex
)
else -> throw RuntimeException("Can't extract default lambda info $it.\n Unknown instruction: $instanceInstuction")
}
}
}