Introduce ExpressionLambda and DefaultLambda abstractions,
extract common part to LambdaInfo
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<CapturedParamDesc>
|
||||
|
||||
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<Type>) : LambdaInfo(false, false) {
|
||||
override val invokeMethod: Method
|
||||
get() = TODO("not implemented")
|
||||
|
||||
override val invokeMethodDescriptor: FunctionDescriptor
|
||||
get() = TODO("not implemented")
|
||||
|
||||
override val capturedVars: List<CapturedParamDesc>
|
||||
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<String>
|
||||
private lateinit var closure: CalculatedClosure
|
||||
val functionDescriptor: FunctionDescriptor
|
||||
val classDescriptor: ClassDescriptor
|
||||
val lambdaClassType: Type
|
||||
private val labels: Set<String>
|
||||
|
||||
var node: SMAPAndMethodNode? = null
|
||||
val propertyReferenceInfo: PropertyReferenceInfo?
|
||||
private lateinit var closure: CalculatedClosure
|
||||
|
||||
init {
|
||||
val bindingContext = typeMapper.bindingContext
|
||||
val function = bindingContext.get<PsiElement, SimpleFunctionDescriptor>(BindingContext.FUNCTION, this.functionWithBodyOrCallableReference)
|
||||
val function = bindingContext.get<PsiElement, SimpleFunctionDescriptor>(BindingContext.FUNCTION, functionWithBodyOrCallableReference)
|
||||
if (function == null && expression is KtCallableReferenceExpression) {
|
||||
val variableDescriptor = bindingContext.get<PsiElement, VariableDescriptor>(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<ClassDescriptor, MutableClosure>(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<CapturedParamDesc> by lazy {
|
||||
override val capturedVars: List<CapturedParamDesc> by lazy {
|
||||
arrayListOf<CapturedParamDesc>().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<Type>
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user