Generate all bodies in interfaces as default methods
This commit is contained in:
@@ -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
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user