Generate all bodies in interfaces as default methods

This commit is contained in:
Michael Bogdanov
2016-09-09 14:52:32 +03:00
parent fb48f70273
commit dfd5be1a33
5 changed files with 106 additions and 15 deletions
@@ -22,6 +22,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.collections.CollectionsKt;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind;
@@ -161,7 +163,9 @@ public class FunctionCodegen {
OwnerKind contextKind = methodContext.getContextKind();
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
contextKind != OwnerKind.DEFAULT_IMPLS) {
(isJvm8InterfaceMember(functionDescriptor, state)
? contextKind == OwnerKind.DEFAULT_IMPLS
: contextKind != OwnerKind.DEFAULT_IMPLS)) {
return;
}
@@ -195,6 +199,10 @@ public class FunctionCodegen {
generateBridges(functionDescriptor);
if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS) {
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
}
boolean staticInCompanionObject = AnnotationUtilKt.isPlatformStaticInCompanionObject(functionDescriptor);
if (staticInCompanionObject) {
ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
@@ -227,12 +235,40 @@ public class FunctionCodegen {
Method accessorMethod =
typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null));
Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration());
generateDelegateToMethodBody(false, mv, accessorMethod, owningType.getInternalName());
generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName());
}
endVisit(mv, null, origin.getElement());
}
private void generateDelegateForDefaultImpl(
@NotNull final FunctionDescriptor functionDescriptor,
@Nullable PsiElement element
) {
Method defaultImplMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS);
CodegenUtilKt.generateMethod(
v, "Default Impl delegate in interface", Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC,
new Method(defaultImplMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, defaultImplMethod.getDescriptor()),
element, JvmDeclarationOrigin.NO_ORIGIN,
state, new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter adapter) {
Method interfaceMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.IMPLEMENTATION);
Type type = typeMapper.mapOwner(functionDescriptor);
generateDelegateToMethodBody(
-1, adapter,
interfaceMethod,
type.getInternalName(),
Opcodes.INVOKESPECIAL,
true
);
return null;
}
}
);
}
private void generateMethodAnnotations(
@NotNull FunctionDescriptor functionDescriptor,
Method asmMethod,
@@ -360,13 +396,16 @@ public class FunctionCodegen {
generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext());
methodEnd = new Label();
}
else if (OwnerKind.IMPLEMENTATION == context.getContextKind() &&
JvmCodegenUtil.isJvmInterface(functionDescriptor.getContainingDeclaration())) {
int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.IMPLEMENTATION, context.getState());
else if (OwnerKind.DEFAULT_IMPLS == context.getContextKind() && isJvm8InterfaceMember(functionDescriptor, parentCodegen.state)) {
int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.DEFAULT_IMPLS, context.getState());
assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor;
Type type = typeMapper.mapDefaultImpls((ClassDescriptor) functionDescriptor.getContainingDeclaration());
Type type = typeMapper.mapOwner(functionDescriptor);
Method asmMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS);
generateDelegateToMethodBody(true, mv, asmMethod, type.getInternalName());
generateDelegateToStaticMethodBody(
true, mv,
new Method(asmMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, asmMethod.getDescriptor()),
type.getInternalName()
);
methodEnd = new Label();
}
else {
@@ -525,14 +564,17 @@ public class FunctionCodegen {
@NotNull Method asmMethod,
@NotNull MultifileClassFacadeContext context
) {
generateDelegateToMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName());
generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName());
}
private static void generateDelegateToMethodBody(
boolean isStatic,
// -1 means to add additional this parameter on stack
int firstParamIndex,
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@NotNull String classToDelegateTo
@NotNull String classToDelegateTo,
int opcode,
boolean isInterface
) {
InstructionAdapter iv = new InstructionAdapter(mv);
Type[] argTypes = asmMethod.getArgumentTypes();
@@ -543,15 +585,29 @@ public class FunctionCodegen {
iv.visitLabel(label);
iv.visitLineNumber(1, label);
int k = isStatic ? 0 : 1;
for (Type argType : argTypes) {
iv.load(k, argType);
k += argType.getSize();
int paramIndex = firstParamIndex;
if (paramIndex == -1) {
iv.load(0, AsmTypes.OBJECT_TYPE);
paramIndex = 1;
}
iv.invokestatic(classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), false);
for (Type argType : argTypes) {
iv.load(paramIndex, argType);
paramIndex += argType.getSize();
}
iv.visitMethodInsn(opcode, classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), isInterface);
iv.areturn(asmMethod.getReturnType());
}
private static void generateDelegateToStaticMethodBody(
boolean isStatic,
@NotNull MethodVisitor mv,
@NotNull Method asmMethod,
@NotNull String classToDelegateTo
) {
generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, false);
}
private static boolean needIndexForVar(JvmMethodParameterKind kind) {
return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE ||
kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
@@ -0,0 +1,13 @@
// JVM_TARGET: 1.8
interface Z {
val z: String;
get() = "OK"
}
class Test : Z
fun box() : String {
return Test().z
}
@@ -0,0 +1,15 @@
// JVM_TARGET: 1.8
interface Test {
var z: String
get() = "OK"
set(value) {}
}
// TESTED_OBJECT_KIND: function
// TESTED_OBJECTS: Test, getZ
// FLAGS: ACC_PUBLIC
// TESTED_OBJECT_KIND: function
// TESTED_OBJECTS: Test, setZ
// FLAGS: ACC_PUBLIC
@@ -41,6 +41,12 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest {
doTest(fileName);
}
@TestMetadata("defaultProperty.kt")
public void testDefaultProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultProperty.kt");
doTest(fileName);
}
@TestMetadata("interfaceAndDefaultImpls.kt")
public void testInterfaceAndDefaultImpls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/interfaceAndDefaultImpls.kt");
@@ -34,6 +34,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
public final class JvmAbi {
public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls";
public static final String DEFAULT_IMPLS_SUFFIX = "$" + DEFAULT_IMPLS_CLASS_NAME;
public static final String DEFAULT_IMPLS_DELEGATE_SUFFIX = "$defaultImpl";
public static final String DEFAULT_PARAMS_IMPL_SUFFIX = "$default";