Add default lambda inlining prototype

This commit is contained in:
Mikhael Bogdanov
2017-05-08 15:28:03 +02:00
parent b7af4ac882
commit 97bcf9f538
23 changed files with 477 additions and 3 deletions
@@ -28,6 +28,7 @@ enum class ValueKind {
DEFAULT_MASK,
METHOD_HANDLE_IN_DEFAULT,
CAPTURED,
DEFAULT_LAMBDA_CAPTURED_PARAMETER
}
abstract class CallGenerator {
@@ -417,6 +417,11 @@ public class InlineCodegen extends CallGenerator {
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex,
DefaultMethodUtilKt.extractDefaultLambdaOffsetAndDescriptor(jvmSignature, functionDescriptor)
);
for (DefaultLambda lambda : defaultLambdas) {
invocationParamBuilder.buildParameters().getParameterByDeclarationSlot(lambda.getOffset()).setLambda(lambda);
LambdaInfo prev = expressionMap.put(lambda.getOffset(), lambda);
assert prev == null : "Lambda with offset " + lambda.getOffset() + " already exists: " + prev;
}
}
ReifiedTypeParametersUsages reificationResult = reifiedTypeInliner.reifyInstructions(node);
generateClosuresBodies();
@@ -668,7 +673,11 @@ public class InlineCodegen extends CallGenerator {
info = invocationParamBuilder.addNextValueParameter(type, false, remappedValue, parameterIndex);
}
recordParameterValueInLocalVal(false, isDefaultParameter, info);
recordParameterValueInLocalVal(
false,
isDefaultParameter || kind == ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER,
info
);
}
}
@@ -826,12 +835,34 @@ public class InlineCodegen extends CallGenerator {
if (next instanceof ExpressionLambda) {
codegen.pushClosureOnStack(((ExpressionLambda) next).getClassDescriptor(), true, this, functionReferenceReceiver);
}
else if (next instanceof DefaultLambda) {
rememberCapturedForDefaultLambda((DefaultLambda) next);
}
else {
//TODO
throw new RuntimeException("Unknown lambda: " + next);
}
activeLambda = null;
}
private void rememberCapturedForDefaultLambda(@NotNull DefaultLambda defaultLambda) {
List<CapturedParamDesc> vars = defaultLambda.getCapturedVars();
int paramIndex = 0;
for (CapturedParamDesc captured : vars) {
putArgumentOrCapturedToLocalVal(
captured.getType(),
//HACK: actually parameter would be placed on stack in default function
// also see ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER check
StackValue.onStack(captured.getType()),
paramIndex,
paramIndex,
ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER
);
paramIndex++;
defaultLambda.getParameterOffsetsInDefault().add(invocationParamBuilder.getNextParameterOffset());
}
}
@NotNull
public static CodegenContext getContext(
@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state, @Nullable KtFile sourceFile
@@ -84,6 +84,8 @@ class DefaultLambda(
val offset: Int
) : LambdaInfo(parameterDescriptor.isCrossinline, false) {
val parameterOffsetsInDefault: MutableList<Int> = arrayListOf()
override lateinit var invokeMethod: Method
private set
@@ -76,6 +76,7 @@ public class LocalVarRemapper {
}
else {
//captured params are not used directly in this inlined method, they are used in closure
//except captured ones for default lambdas, they are generated in default body
remappedIndex = actualParamsSize - params.getArgsSizeOnStack() + index;
}
@@ -332,6 +332,22 @@ class MethodInliner(
}
}
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
if (InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL == owner) {
val index = name.substringAfter(InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL).toInt()
val lambda = getLambdaIfExists(index) as DefaultLambda
lambda.parameterOffsetsInDefault.zip(lambda.capturedVars).asReversed().forEach {
(_, captured) ->
super.visitFieldInsn(
Opcodes.PUTSTATIC, captured.containingLambdaName, "$$$" + captured.fieldName, captured.type.descriptor
)
}
}
else {
super.visitMethodInsn(opcode, owner, name, desc, itf)
}
}
override fun visitLocalVariable(
name: String, desc: String, signature: String?, start: Label, end: Label, index: Int
) {
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
@@ -62,7 +63,12 @@ public class RemapVisitor extends MethodBodyVisitor {
FieldInsnNode fin = new FieldInsnNode(opcode, owner, name, desc);
StackValue inline = nodeRemapper.getFieldForInline(fin, null);
assert inline != null : "Captured field should have not null stackValue " + fin;
inline.put(inline.type, this);
if (Opcodes.PUTSTATIC == opcode) {
inline.store(StackValue.onStack(inline.type), this);
}
else {
inline.put(inline.type, this);
}
return;
}
super.visitFieldInsn(opcode, owner, name, desc);