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() {
|
private void generateClosuresBodies() {
|
||||||
for (LambdaInfo info : expressionMap.values()) {
|
for (LambdaInfo info : expressionMap.values()) {
|
||||||
info.setNode(generateLambdaBody(info));
|
info.generateLambdaBody(codegen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private SMAPAndMethodNode generateLambdaBody(@NotNull LambdaInfo info) {
|
public static SMAP generateMethodBody(
|
||||||
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(
|
|
||||||
@NotNull MethodVisitor adapter,
|
@NotNull MethodVisitor adapter,
|
||||||
@NotNull FunctionDescriptor descriptor,
|
@NotNull FunctionDescriptor descriptor,
|
||||||
@NotNull MethodContext context,
|
@NotNull MethodContext context,
|
||||||
@NotNull KtExpression expression,
|
@NotNull KtExpression expression,
|
||||||
@NotNull JvmMethodSignature jvmMethodSignature,
|
@NotNull JvmMethodSignature jvmMethodSignature,
|
||||||
@NotNull ExpressionCodegen codegen,
|
@NotNull ExpressionCodegen codegen,
|
||||||
@Nullable LambdaInfo lambdaInfo
|
@Nullable ExpressionLambda lambdaInfo
|
||||||
) {
|
) {
|
||||||
boolean isLambda = lambdaInfo != null;
|
boolean isLambda = lambdaInfo != null;
|
||||||
GenerationState state = codegen.getState();
|
GenerationState state = codegen.getState();
|
||||||
@@ -806,7 +782,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText();
|
assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText();
|
||||||
|
|
||||||
LambdaInfo info =
|
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());
|
ParameterInfo closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.getIndex());
|
||||||
closureInfo.setLambda(info);
|
closureInfo.setLambda(info);
|
||||||
@@ -837,14 +813,19 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
private void putClosureParametersOnStack() {
|
private void putClosureParametersOnStack() {
|
||||||
for (LambdaInfo next : expressionMap.values()) {
|
for (LambdaInfo next : expressionMap.values()) {
|
||||||
//closure parameters for bounded callable references are generated inplace
|
//closure parameters for bounded callable references are generated inplace
|
||||||
if (next.isBoundCallableReference()) continue;
|
if (next.isBoundCallableReference) continue;
|
||||||
putClosureParametersOnStack(next, null);
|
putClosureParametersOnStack(next, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putClosureParametersOnStack(@NotNull LambdaInfo next, @Nullable StackValue functionReferenceReceiver) {
|
private void putClosureParametersOnStack(@NotNull LambdaInfo next, @Nullable StackValue functionReferenceReceiver) {
|
||||||
activeLambda = next;
|
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;
|
activeLambda = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
|
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
|
||||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
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.calls.callUtil.getResolvedCallWithAssert
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
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,
|
expression: KtExpression,
|
||||||
private val typeMapper: KotlinTypeMapper,
|
private val typeMapper: KotlinTypeMapper,
|
||||||
@JvmField val isCrossInline: Boolean,
|
isCrossInline: Boolean,
|
||||||
val isBoundCallableReference: Boolean
|
isBoundCallableReference: Boolean
|
||||||
) : LabelOwner {
|
) : 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 functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
|
||||||
|
|
||||||
val labels: Set<String>
|
private val labels: Set<String>
|
||||||
private lateinit var closure: CalculatedClosure
|
|
||||||
val functionDescriptor: FunctionDescriptor
|
|
||||||
val classDescriptor: ClassDescriptor
|
|
||||||
val lambdaClassType: Type
|
|
||||||
|
|
||||||
var node: SMAPAndMethodNode? = null
|
private lateinit var closure: CalculatedClosure
|
||||||
val propertyReferenceInfo: PropertyReferenceInfo?
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val bindingContext = typeMapper.bindingContext
|
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) {
|
if (function == null && expression is KtCallableReferenceExpression) {
|
||||||
val variableDescriptor = bindingContext.get<PsiElement, VariableDescriptor>(BindingContext.VARIABLE, this.functionWithBodyOrCallableReference)
|
val variableDescriptor =
|
||||||
assert(variableDescriptor is VariableDescriptorWithAccessors) { "Reference expression not resolved to variable descriptor with accessors: " + expression.getText() }
|
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!!)
|
classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor!!)
|
||||||
lambdaClassType = typeMapper.mapClass(classDescriptor)
|
lambdaClassType = typeMapper.mapClass(classDescriptor)
|
||||||
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
|
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
|
||||||
functionDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
|
invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
|
||||||
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
|
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
|
||||||
propertyReferenceInfo = PropertyReferenceInfo(
|
propertyReferenceInfo = PropertyReferenceInfo(
|
||||||
resolvedCall.resultingDescriptor as VariableDescriptor, getFunction
|
resolvedCall.resultingDescriptor as VariableDescriptor, getFunction
|
||||||
@@ -71,10 +129,9 @@ class LambdaInfo(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
propertyReferenceInfo = null
|
propertyReferenceInfo = null
|
||||||
assert(function != null) { "Function is not resolved to descriptor: " + expression.text }
|
invokeMethodDescriptor = function ?: throw AssertionError("Function is not resolved to descriptor: " + expression.text)
|
||||||
functionDescriptor = function!!
|
classDescriptor = anonymousClassForCallable(bindingContext, invokeMethodDescriptor)
|
||||||
classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor)
|
lambdaClassType = asmTypeForAnonymousClass(bindingContext, invokeMethodDescriptor)
|
||||||
lambdaClassType = asmTypeForAnonymousClass(bindingContext, functionDescriptor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingContext.get<ClassDescriptor, MutableClosure>(CLOSURE, classDescriptor).let {
|
bindingContext.get<ClassDescriptor, MutableClosure>(CLOSURE, classDescriptor).let {
|
||||||
@@ -82,10 +139,11 @@ class LambdaInfo(
|
|||||||
closure = it!!
|
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 {
|
arrayListOf<CapturedParamDesc>().apply {
|
||||||
if (closure.captureThis != null) {
|
if (closure.captureThis != null) {
|
||||||
val type = typeMapper.mapType(closure.captureThis!!)
|
val type = typeMapper.mapType(closure.captureThis!!)
|
||||||
@@ -108,34 +166,40 @@ class LambdaInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
closure.captureVariables.values.forEach {
|
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 {
|
override fun isMyLabel(name: String): Boolean {
|
||||||
return labels.contains(name)
|
return labels.contains(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isPropertyReference: Boolean
|
val isPropertyReference: Boolean
|
||||||
get() = propertyReferenceInfo != null
|
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
|
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.
|
// 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.
|
// Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated.
|
||||||
visitInsn(Opcodes.NOP);
|
visitInsn(Opcodes.NOP);
|
||||||
@@ -256,9 +256,8 @@ public class MethodInliner {
|
|||||||
result.getReifiedTypeParametersUsages().mergeAll(lambdaResult.getReifiedTypeParametersUsages());
|
result.getReifiedTypeParametersUsages().mergeAll(lambdaResult.getReifiedTypeParametersUsages());
|
||||||
|
|
||||||
//return value boxing/unboxing
|
//return value boxing/unboxing
|
||||||
Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor()));
|
Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getInvokeMethodDescriptor()));
|
||||||
Method delegate = typeMapper.mapAsmMethod(info.getFunctionDescriptor());
|
StackValue.onStack(info.getInvokeMethod().getReturnType()).put(bridge.getReturnType(), this);
|
||||||
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
|
|
||||||
setLambdaInlining(false);
|
setLambdaInlining(false);
|
||||||
addInlineMarker(this, false);
|
addInlineMarker(this, false);
|
||||||
mapper.endMapping();
|
mapper.endMapping();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.jetbrains.kotlin.codegen.StackValue;
|
import org.jetbrains.kotlin.codegen.StackValue;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
|
|
||||||
class ParameterInfo {
|
public class ParameterInfo {
|
||||||
private final int index;
|
private final int index;
|
||||||
public final int declarationIndex;
|
public final int declarationIndex;
|
||||||
public final Type type;
|
public final Type type;
|
||||||
|
|||||||
Reference in New Issue
Block a user