Introduce ExpressionLambda and DefaultLambda abstractions,

extract common part to LambdaInfo
This commit is contained in:
Mikhael Bogdanov
2017-05-04 12:01:43 +02:00
parent 9c51392aff
commit 70e550e984
4 changed files with 125 additions and 81 deletions
@@ -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;