Add support for contract feature in inliner

This commit is contained in:
Mikhael Bogdanov
2017-12-14 17:41:49 +01:00
parent 4890979476
commit 819a3a95b3
15 changed files with 610 additions and 72 deletions
@@ -68,7 +68,7 @@ open class FieldRemapper(
val insnNode = capturedFieldAccess[currentInstruction] as FieldInsnNode
if (canProcess(insnNode.owner, insnNode.name, true)) {
insnNode.name = CAPTURED_FIELD_FOLD_PREFIX + getFieldNameForFolding(insnNode)
insnNode.name = Companion.foldName(getFieldNameForFolding(insnNode))
insnNode.opcode = Opcodes.GETSTATIC
node.remove(InsnSequence(capturedFieldAccess[0], insnNode))
@@ -95,4 +95,9 @@ open class FieldRemapper(
open fun getFieldForInline(node: FieldInsnNode, prefix: StackValue?): StackValue? =
MethodInliner.findCapturedField(node, this).remapValue
companion object {
fun foldName(fieldName: String) =
CAPTURED_FIELD_FOLD_PREFIX + fieldName
}
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMet
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
@@ -410,85 +411,111 @@ class MethodInliner(
val frame = sources[instructions.indexOf(cur)]
if (frame != null) {
if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) {
awaitClassReification = true
}
else if (cur is MethodInsnNode) {
if (isFinallyStart(cur)) {
//TODO deep index calc could be more precise
currentFinallyDeep = getConstant(cur.previous)
}
when {
ReifiedTypeInliner.isNeedClassReificationMarker(cur) -> awaitClassReification = true
val owner = cur.owner
val name = cur.name
//TODO check closure
val argTypes = Type.getArgumentTypes(cur.desc)
val paramCount = argTypes.size + 1//non static
val firstParameterIndex = frame.stackSize - paramCount
if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
val sourceValue = frame.getStack(firstParameterIndex)
val lambdaInfo = getLambdaIfExistsAndMarkInstructions(sourceValue, true, instructions, sources, toDelete)
invokeCalls.add(InvokeCall(lambdaInfo, currentFinallyDeep))
}
else if (isSamWrapperConstructorCall(owner, name)) {
transformations.add(SamWrapperTransformationInfo(owner, inliningContext, isAlreadyRegenerated(owner)))
}
else if (isAnonymousConstructorCall(owner, name)) {
val lambdaMapping = HashMap<Int, LambdaInfo>()
var offset = 0
var capturesAnonymousObjectThatMustBeRegenerated = false
for (i in 0 until paramCount) {
val sourceValue = frame.getStack(firstParameterIndex + i)
val lambdaInfo = getLambdaIfExistsAndMarkInstructions(sourceValue, false, instructions, sources, toDelete
)
if (lambdaInfo != null) {
lambdaMapping.put(offset, lambdaInfo)
}
else if (i < argTypes.size && isAnonymousClassThatMustBeRegenerated(argTypes[i])) {
capturesAnonymousObjectThatMustBeRegenerated = true
}
offset += if (i == 0) 1 else argTypes[i - 1].size
cur is MethodInsnNode -> {
if (isFinallyStart(cur)) {
//TODO deep index calc could be more precise
currentFinallyDeep = getConstant(cur.previous)
}
transformations.add(
buildConstructorInvocation(
owner, cur.desc, lambdaMapping, awaitClassReification, capturesAnonymousObjectThatMustBeRegenerated
val owner = cur.owner
val name = cur.name
//TODO check closure
val argTypes = Type.getArgumentTypes(cur.desc)
val paramCount = argTypes.size + 1//non static
val firstParameterIndex = frame.stackSize - paramCount
if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
val sourceValue = frame.getStack(firstParameterIndex)
val lambdaInfo = getLambdaIfExistsAndMarkInstructions(sourceValue, true, instructions, sources, toDelete)
invokeCalls.add(InvokeCall(lambdaInfo, currentFinallyDeep))
}
else if (isSamWrapperConstructorCall(owner, name)) {
transformations.add(SamWrapperTransformationInfo(owner, inliningContext, isAlreadyRegenerated(owner)))
}
else if (isAnonymousConstructorCall(owner, name)) {
val lambdaMapping = HashMap<Int, LambdaInfo>()
var offset = 0
var capturesAnonymousObjectThatMustBeRegenerated = false
for (i in 0 until paramCount) {
val sourceValue = frame.getStack(firstParameterIndex + i)
val lambdaInfo = getLambdaIfExistsAndMarkInstructions(sourceValue, false, instructions, sources, toDelete
)
)
awaitClassReification = false
if (lambdaInfo != null) {
lambdaMapping.put(offset, lambdaInfo)
}
else if (i < argTypes.size && isAnonymousClassThatMustBeRegenerated(argTypes[i])) {
capturesAnonymousObjectThatMustBeRegenerated = true
}
offset += if (i == 0) 1 else argTypes[i - 1].size
}
transformations.add(
buildConstructorInvocation(
owner, cur.desc, lambdaMapping, awaitClassReification, capturesAnonymousObjectThatMustBeRegenerated
)
)
awaitClassReification = false
}
else if (inliningContext.isInliningLambda && ReifiedTypeInliner.isOperationReifiedMarker(cur)) {
val reificationArgument = cur.reificationArgument
val parameterName = reificationArgument!!.parameterName
result.reifiedTypeParametersUsages.addUsedReifiedParameter(parameterName)
}
}
else if (inliningContext.isInliningLambda && ReifiedTypeInliner.isOperationReifiedMarker(cur)) {
val reificationArgument = cur.reificationArgument
val parameterName = reificationArgument!!.parameterName
result.reifiedTypeParametersUsages.addUsedReifiedParameter(parameterName)
cur.opcode == Opcodes.GETSTATIC -> {
val fieldInsnNode = cur as FieldInsnNode?
val className = fieldInsnNode!!.owner
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) {
transformations.add(
AnonymousObjectTransformationInfo(
className, awaitClassReification, isAlreadyRegenerated(className), true,
inliningContext.nameGenerator
)
)
awaitClassReification = false
}
else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
transformations.add(
WhenMappingTransformationInfo(
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
)
)
}
}
}
else if (cur.opcode == Opcodes.GETSTATIC) {
val fieldInsnNode = cur as FieldInsnNode?
val className = fieldInsnNode!!.owner
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) {
transformations.add(
AnonymousObjectTransformationInfo(
className, awaitClassReification, isAlreadyRegenerated(className), true,
inliningContext.nameGenerator
)
)
awaitClassReification = false
}
else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
transformations.add(
WhenMappingTransformationInfo(
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
)
)
}
}
else if (cur.opcode == Opcodes.POP) {
getLambdaIfExistsAndMarkInstructions(frame.top()!!, true, instructions, sources, toDelete)?.let {
cur.opcode == Opcodes.POP -> getLambdaIfExistsAndMarkInstructions(frame.top()!!, true, instructions, sources, toDelete)?.let {
toDelete.add(cur)
}
cur.opcode == Opcodes.PUTFIELD -> {
//Recognize next contract's pattern in inline lambda
// ALOAD 0
// SOME_VALUE
// PUTFIELD $capturedField
// and transform it to
// SOME_VALUE
// PUTSTATIC $$$$capturedField
val fieldInsn = cur as FieldInsnNode
if (isCapturedFieldName(fieldInsn.name) &&
nodeRemapper is InlinedLambdaRemapper &&
nodeRemapper.originalLambdaInternalName == fieldInsn.owner) {
val stackTransformations = mutableSetOf<AbstractInsnNode>()
val lambdaInfo = getLambdaIfExistsAndMarkInstructions(frame.peek(1)!!, false, instructions, sources, stackTransformations)
if (lambdaInfo != null && stackTransformations.all { it is VarInsnNode }) {
assert(lambdaInfo.lambdaClassType.internalName == nodeRemapper.originalLambdaInternalName) {
"Wrong bytecode template for contract template: ${lambdaInfo.lambdaClassType.internalName} != ${nodeRemapper.originalLambdaInternalName}"
}
fieldInsn.name = FieldRemapper.foldName(fieldInsn.name)
fieldInsn.opcode = Opcodes.PUTSTATIC
toDelete.addAll(stackTransformations)
}
}
}
}
}
else {