From 70e550e984165752ff3bb16293da8152c68b2092 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Thu, 4 May 2017 12:01:43 +0200 Subject: [PATCH] Introduce ExpressionLambda and DefaultLambda abstractions, extract common part to LambdaInfo --- .../kotlin/codegen/inline/InlineCodegen.java | 41 ++--- .../kotlin/codegen/inline/LambdaInfo.kt | 154 +++++++++++++----- .../kotlin/codegen/inline/MethodInliner.java | 9 +- .../kotlin/codegen/inline/ParameterInfo.java | 2 +- 4 files changed, 125 insertions(+), 81 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index e2401dac24c..07574828894 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -514,43 +514,19 @@ public class InlineCodegen extends CallGenerator { private void generateClosuresBodies() { for (LambdaInfo info : expressionMap.values()) { - info.setNode(generateLambdaBody(info)); + info.generateLambdaBody(codegen); } } @NotNull - private SMAPAndMethodNode generateLambdaBody(@NotNull LambdaInfo info) { - KtExpression declaration = info.getFunctionWithBodyOrCallableReference(); - FunctionDescriptor descriptor = info.getFunctionDescriptor(); - - ClassContext closureContext = info.isPropertyReference() - ? codegen.getContext().intoAnonymousClass(info.getClassDescriptor(), codegen, OwnerKind.IMPLEMENTATION) - : codegen.getContext().intoClosure(descriptor, codegen, typeMapper); - MethodContext context = closureContext.intoInlinedLambda(descriptor, info.isCrossInline, info.isPropertyReference()); - - JvmMethodSignature jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(descriptor); - Method asmMethod = jvmMethodSignature.getAsmMethod(); - MethodNode methodNode = new MethodNode( - InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind(), state), - asmMethod.getName(), asmMethod.getDescriptor(), null, null - ); - - MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode); - - SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, codegen, info); - adapter.visitMaxs(-1, -1); - return new SMAPAndMethodNode(methodNode, smap); - } - - @NotNull - private static SMAP generateMethodBody( + public static SMAP generateMethodBody( @NotNull MethodVisitor adapter, @NotNull FunctionDescriptor descriptor, @NotNull MethodContext context, @NotNull KtExpression expression, @NotNull JvmMethodSignature jvmMethodSignature, @NotNull ExpressionCodegen codegen, - @Nullable LambdaInfo lambdaInfo + @Nullable ExpressionLambda lambdaInfo ) { boolean isLambda = lambdaInfo != null; GenerationState state = codegen.getState(); @@ -806,7 +782,7 @@ public class InlineCodegen extends CallGenerator { assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText(); LambdaInfo info = - new LambdaInfo(lambda, typeMapper, parameter.isCrossinline(), getBoundCallableReferenceReceiver(expression) != null); + new ExpressionLambda(lambda, typeMapper, parameter.isCrossinline(), getBoundCallableReferenceReceiver(expression) != null); ParameterInfo closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.getIndex()); closureInfo.setLambda(info); @@ -837,14 +813,19 @@ public class InlineCodegen extends CallGenerator { private void putClosureParametersOnStack() { for (LambdaInfo next : expressionMap.values()) { //closure parameters for bounded callable references are generated inplace - if (next.isBoundCallableReference()) continue; + if (next.isBoundCallableReference) continue; putClosureParametersOnStack(next, null); } } private void putClosureParametersOnStack(@NotNull LambdaInfo next, @Nullable StackValue functionReferenceReceiver) { activeLambda = next; - codegen.pushClosureOnStack(next.getClassDescriptor(), true, this, functionReferenceReceiver); + if (next instanceof ExpressionLambda) { + codegen.pushClosureOnStack(((ExpressionLambda) next).getClassDescriptor(), true, this, functionReferenceReceiver); + } + else { + //TODO + } activeLambda = null; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index 3cb8c16d755..8b8f9d6e436 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -17,9 +17,7 @@ package org.jetbrains.kotlin.codegen.inline import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.codegen.AsmUtil -import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen -import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.binding.CalculatedClosure import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.binding.CodegenBinding.* @@ -34,36 +32,96 @@ import org.jetbrains.kotlin.resolve.BindingContext 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.FieldInsnNode -import java.util.* +import org.jetbrains.org.objectweb.asm.tree.MethodNode -class LambdaInfo( +abstract class LambdaInfo(@JvmField val isCrossInline: Boolean, @JvmField val isBoundCallableReference: Boolean) : LabelOwner { + + abstract val lambdaClassType: Type + + abstract val invokeMethod: Method + + abstract val invokeMethodDescriptor: FunctionDescriptor + + abstract val capturedVars: List + + lateinit var node: SMAPAndMethodNode + + abstract fun generateLambdaBody(codegen: ExpressionCodegen) + + fun addAllParameters(remapper: FieldRemapper): Parameters { + val builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, invokeMethod.descriptor, this) + + for (info in capturedVars) { + val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, "")) ?: + error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName) + builder.addCapturedParam(field, info.fieldName) + } + + return builder.buildParameters() + } + + + companion object { + fun LambdaInfo.getCapturedParamInfo(descriptor: EnclosedValueDescriptor): CapturedParamDesc { + return CapturedParamDesc(lambdaClassType, descriptor.fieldName, descriptor.type) + } + } +} + + +class DefaultLambda(override val lambdaClassType: Type, lambdaArgs: Array) : LambdaInfo(false, false) { + override val invokeMethod: Method + get() = TODO("not implemented") + + override val invokeMethodDescriptor: FunctionDescriptor + get() = TODO("not implemented") + + override val capturedVars: List + get() = TODO("not implemented") + + override fun isMyLabel(name: String): Boolean = false + + override fun generateLambdaBody(codegen: ExpressionCodegen) { + TODO("not implemented") + } +} + +class ExpressionLambda( expression: KtExpression, private val typeMapper: KotlinTypeMapper, - @JvmField val isCrossInline: Boolean, - val isBoundCallableReference: Boolean -) : LabelOwner { + isCrossInline: Boolean, + isBoundCallableReference: Boolean +) : LambdaInfo(isCrossInline, isBoundCallableReference) { + + override val lambdaClassType: Type + + override val invokeMethod: Method + + override val invokeMethodDescriptor: FunctionDescriptor + + val classDescriptor: ClassDescriptor + + val propertyReferenceInfo: PropertyReferenceInfo? + val functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression - val labels: Set - private lateinit var closure: CalculatedClosure - val functionDescriptor: FunctionDescriptor - val classDescriptor: ClassDescriptor - val lambdaClassType: Type + private val labels: Set - var node: SMAPAndMethodNode? = null - val propertyReferenceInfo: PropertyReferenceInfo? + private lateinit var closure: CalculatedClosure init { val bindingContext = typeMapper.bindingContext - val function = bindingContext.get(BindingContext.FUNCTION, this.functionWithBodyOrCallableReference) + val function = bindingContext.get(BindingContext.FUNCTION, functionWithBodyOrCallableReference) if (function == null && expression is KtCallableReferenceExpression) { - val variableDescriptor = bindingContext.get(BindingContext.VARIABLE, this.functionWithBodyOrCallableReference) - assert(variableDescriptor is VariableDescriptorWithAccessors) { "Reference expression not resolved to variable descriptor with accessors: " + expression.getText() } + val variableDescriptor = + bindingContext.get(BindingContext.VARIABLE, functionWithBodyOrCallableReference) as? VariableDescriptorWithAccessors ?: + throw AssertionError("""Reference expression not resolved to variable descriptor with accessors: ${expression.getText()}""") classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor!!) lambdaClassType = typeMapper.mapClass(classDescriptor) val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor) - functionDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor) + invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor) val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext) propertyReferenceInfo = PropertyReferenceInfo( resolvedCall.resultingDescriptor as VariableDescriptor, getFunction @@ -71,10 +129,9 @@ class LambdaInfo( } else { propertyReferenceInfo = null - assert(function != null) { "Function is not resolved to descriptor: " + expression.text } - functionDescriptor = function!! - classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor) - lambdaClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor) + invokeMethodDescriptor = function ?: throw AssertionError("Function is not resolved to descriptor: " + expression.text) + classDescriptor = anonymousClassForCallable(bindingContext, invokeMethodDescriptor) + lambdaClassType = asmTypeForAnonymousClass(bindingContext, invokeMethodDescriptor) } bindingContext.get(CLOSURE, classDescriptor).let { @@ -82,10 +139,11 @@ class LambdaInfo( closure = it!! } - labels = InlineCodegen.getDeclarationLabels(expression, functionDescriptor) + labels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor) + invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor) } - val capturedVars: List by lazy { + override val capturedVars: List by lazy { arrayListOf().apply { if (closure.captureThis != null) { val type = typeMapper.mapType(closure.captureThis!!) @@ -108,34 +166,40 @@ class LambdaInfo( } closure.captureVariables.values.forEach { - descriptor -> add(getCapturedParamInfo(descriptor)) + descriptor -> + add(getCapturedParamInfo(descriptor)) } } } - private fun getCapturedParamInfo(descriptor: EnclosedValueDescriptor): CapturedParamDesc { - return CapturedParamDesc(lambdaClassType, descriptor.fieldName, descriptor.type) - } - - val invokeParamsWithoutCaptured: List - get() = Arrays.asList(*typeMapper.mapAsmMethod(functionDescriptor).argumentTypes) - - fun addAllParameters(remapper: FieldRemapper): Parameters { - val asmMethod = typeMapper.mapAsmMethod(functionDescriptor) - val builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, asmMethod.descriptor, this) - - for (info in capturedVars) { - val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, "")) ?: error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName) - builder.addCapturedParam(field, info.fieldName) - } - - return builder.buildParameters() - } - override fun isMyLabel(name: String): Boolean { return labels.contains(name) } val isPropertyReference: Boolean get() = propertyReferenceInfo != null + + override fun generateLambdaBody(codegen: ExpressionCodegen) { + val closureContext = + if (isPropertyReference) + codegen.getContext().intoAnonymousClass(classDescriptor, codegen, OwnerKind.IMPLEMENTATION) + else + codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, typeMapper) + val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, isCrossInline, isPropertyReference) + + val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor) + val asmMethod = jvmMethodSignature.asmMethod + val methodNode = MethodNode( + InlineCodegenUtil.API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, context.contextKind, codegen.state), + asmMethod.name, asmMethod.descriptor, null, null + ) + + node = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode).let { adapter -> + val smap = InlineCodegen.generateMethodBody( + adapter, invokeMethodDescriptor, context, functionWithBodyOrCallableReference, jvmMethodSignature, codegen, this + ) + adapter.visitMaxs(-1, -1) + SMAPAndMethodNode(methodNode, smap) + } + } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java index 2a0bd1ed2e3..e8a890f5062 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.java @@ -221,9 +221,9 @@ public class MethodInliner { } int valueParamShift = Math.max(getNextLocalIndex(), markerShift);//NB: don't inline cause it changes - putStackValuesIntoLocals(info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc); + putStackValuesIntoLocals(Arrays.asList(info.getInvokeMethod().getArgumentTypes()), valueParamShift, this, desc); - if (invokeCall.lambdaInfo.getFunctionDescriptor().getValueParameters().isEmpty()) { + if (invokeCall.lambdaInfo.getInvokeMethodDescriptor().getValueParameters().isEmpty()) { // There won't be no parameters processing and line call can be left without actual instructions. // Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated. visitInsn(Opcodes.NOP); @@ -256,9 +256,8 @@ public class MethodInliner { result.getReifiedTypeParametersUsages().mergeAll(lambdaResult.getReifiedTypeParametersUsages()); //return value boxing/unboxing - Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor())); - Method delegate = typeMapper.mapAsmMethod(info.getFunctionDescriptor()); - StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this); + Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getInvokeMethodDescriptor())); + StackValue.onStack(info.getInvokeMethod().getReturnType()).put(bridge.getReturnType(), this); setLambdaInlining(false); addInlineMarker(this, false); mapper.endMapping(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ParameterInfo.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ParameterInfo.java index dd48bd9e97b..ab985de79dd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ParameterInfo.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ParameterInfo.java @@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.StackValue; import org.jetbrains.org.objectweb.asm.Type; -class ParameterInfo { +public class ParameterInfo { private final int index; public final int declarationIndex; public final Type type;