Generate delegation body for default interface methods, calculate proper abstractness for them

This commit is contained in:
Michael Bogdanov
2016-04-12 16:24:05 +03:00
committed by Mikhael Bogdanov
parent fc8058848f
commit 01aa89b1ea
6 changed files with 101 additions and 15 deletions
@@ -168,9 +168,9 @@ public class AsmUtil {
return new Method(name, Type.getMethodDescriptor(returnType, parameterTypes));
}
public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind) {
return (functionDescriptor.getModality() == Modality.ABSTRACT
|| isJvmInterface(functionDescriptor.getContainingDeclaration()))
public static boolean isAbstractMethod(FunctionDescriptor functionDescriptor, OwnerKind kind, GenerationState state) {
return (functionDescriptor.getModality() == Modality.ABSTRACT ||
(isJvmInterface(functionDescriptor.getContainingDeclaration()) && !state.isJvm8Target()))
&& !isStaticMethod(kind, functionDescriptor);
}
@@ -184,7 +184,7 @@ public class AsmUtil {
return kind == OwnerKind.PACKAGE || kind == OwnerKind.DEFAULT_IMPLS;
}
public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind) {
public static int getMethodAsmFlags(FunctionDescriptor functionDescriptor, OwnerKind kind, GenerationState state) {
int flags = getCommonCallableFlags(functionDescriptor);
for (AnnotationCodegen.JvmFlagAnnotation flagAnnotation : AnnotationCodegen.METHOD_FLAGS) {
@@ -214,7 +214,7 @@ public class AsmUtil {
flags |= ACC_STATIC;
}
if (isAbstractMethod(functionDescriptor, kind)) {
if (isAbstractMethod(functionDescriptor, kind, state)) {
flags |= ACC_ABSTRACT;
}
@@ -63,10 +63,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
@@ -163,7 +160,7 @@ public class FunctionCodegen {
JvmMethodGenericSignature jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, contextKind);
Method asmMethod = jvmSignature.getAsmMethod();
int flags = getMethodAsmFlags(functionDescriptor, contextKind);
int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);
if (origin.getOriginKind() == JvmDeclarationOriginKind.SAM_DELEGATION) {
flags |= ACC_SYNTHETIC;
@@ -198,7 +195,7 @@ public class FunctionCodegen {
parentBodyCodegen.addAdditionalTask(new JvmStaticGenerator(functionDescriptor, origin, state, parentBodyCodegen));
}
if (state.getClassBuilderMode() != ClassBuilderMode.FULL || isAbstractMethod(functionDescriptor, contextKind)) {
if (state.getClassBuilderMode() != ClassBuilderMode.FULL || isAbstractMethod(functionDescriptor, contextKind, state)) {
generateLocalVariableTable(
mv,
jvmSignature,
@@ -356,6 +353,15 @@ 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());
assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor;
Type type = typeMapper.mapDefaultImpls((ClassDescriptor) functionDescriptor.getContainingDeclaration());
Method asmMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS);
generateDelegateToMethodBody(true, mv, asmMethod, type.getInternalName());
methodEnd = new Label();
}
else {
FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, isStaticMethod(context.getContextKind(),
functionDescriptor));
@@ -600,7 +606,7 @@ public class FunctionCodegen {
}
}
if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION)) {
if (!descriptor.getKind().isReal() && isAbstractMethod(descriptor, OwnerKind.IMPLEMENTATION, state)) {
CallableDescriptor overridden = SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor);
assert overridden != null;
@@ -310,7 +310,7 @@ public class InlineCodegen extends CallGenerator {
MethodNode node = new MethodNode(
InlineCodegenUtil.API,
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
getMethodAsmFlags(functionDescriptor, context.getContextKind(), state) | (callDefault ? Opcodes.ACC_STATIC : 0),
asmMethod.getName(),
asmMethod.getDescriptor(),
null, null
@@ -474,7 +474,7 @@ public class InlineCodegen extends CallGenerator {
JvmMethodSignature jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(descriptor);
Method asmMethod = jvmMethodSignature.getAsmMethod();
MethodNode methodNode = new MethodNode(
InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()),
InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind(), state),
asmMethod.getName(), asmMethod.getDescriptor(), null, null
);
@@ -678,7 +678,7 @@ public class InlineCodegen extends CallGenerator {
@Override
public void putHiddenParams() {
if ((getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) == 0) {
if ((getMethodAsmFlags(functionDescriptor, context.getContextKind(), state) & Opcodes.ACC_STATIC) == 0) {
invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false);
}
@@ -0,0 +1,16 @@
// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET
interface Test {
fun test(): String {
return "OK"
}
fun testAbstract(): String
}
// TESTED_OBJECT_KIND: function
// TESTED_OBJECTS: Test, test
// FLAGS: ACC_PUBLIC
// TESTED_OBJECT_KIND: function
// TESTED_OBJECTS: Test, testAbstract
// FLAGS: ACC_PUBLIC, ACC_ABSTRACT
@@ -0,0 +1,15 @@
// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET
interface Test {
fun a() {
}
}
// TESTED_OBJECT_KIND: class
// TESTED_OBJECTS: Test$DefaultImpls
// FLAGS: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
// TESTED_OBJECT_KIND: class
// TESTED_OBJECTS: Test
// FLAGS: ACC_PUBLIC, ACC_ABSTRACT, ACC_INTERFACE
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.flags;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/java8/writeFlags")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest {
public void testAllFilesPresentInWriteFlags() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/writeFlags"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("defaultMethod.kt")
public void testDefaultMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultMethod.kt");
doTest(fileName);
}
@TestMetadata("interfaceAndDefaultImpls.kt")
public void testInterfaceAndDefaultImpls() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/interfaceAndDefaultImpls.kt");
doTest(fileName);
}
}