Support equals/hashCode for fun interfaces in JVM and JVM_IR
#KT-33455 Fixed
This commit is contained in:
committed by
Alexander Udalov
parent
de461dd9a5
commit
9fa8e009c6
@@ -1010,7 +1010,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private StackValue genClosure(KtDeclarationWithBody declaration, @Nullable SamType samType) {
|
private StackValue genClosure(KtDeclarationWithBody declaration, @Nullable SamType samType) {
|
||||||
FunctionDescriptor descriptor = bindingContext.get(FUNCTION, declaration);
|
FunctionDescriptor descriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
|
||||||
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
|
||||||
|
|
||||||
return genClosure(
|
return genClosure(
|
||||||
@@ -3280,7 +3280,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
StackValue receiver = generateCallableReferenceReceiver(resolvedCall);
|
StackValue receiver = generateCallableReferenceReceiver(resolvedCall);
|
||||||
|
|
||||||
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
|
FunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression);
|
||||||
if (functionDescriptor != null) {
|
if (functionDescriptor != null) {
|
||||||
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(
|
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(
|
||||||
state, functionDescriptor, resolvedCall,
|
state, functionDescriptor, resolvedCall,
|
||||||
|
|||||||
@@ -25,27 +25,31 @@ import org.jetbrains.kotlin.descriptors.*;
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl;
|
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||||
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label;
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.Method;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.NO_FLAG_PACKAGE_PRIVATE;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses;
|
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
|
|
||||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
public class SamWrapperCodegen {
|
public class SamWrapperCodegen {
|
||||||
public static final String FUNCTION_FIELD_NAME = "function";
|
private static final String FUNCTION_FIELD_NAME = "function";
|
||||||
|
private static final Method GET_FUNCTION_DELEGATE = new Method("getFunctionDelegate", FUNCTION, new Type[0]);
|
||||||
|
|
||||||
private final GenerationState state;
|
private final GenerationState state;
|
||||||
private final boolean isInsideInline;
|
private final boolean isInsideInline;
|
||||||
@@ -81,6 +85,8 @@ public class SamWrapperCodegen {
|
|||||||
// e.g. (T, T) -> Int
|
// e.g. (T, T) -> Int
|
||||||
KotlinType functionType = samType.getKotlinFunctionType();
|
KotlinType functionType = samType.getKotlinFunctionType();
|
||||||
|
|
||||||
|
boolean isKotlinFunInterface = !(samType.getClassDescriptor() instanceof JavaClassDescriptor);
|
||||||
|
|
||||||
ClassDescriptor classDescriptor = new ClassDescriptorImpl(
|
ClassDescriptor classDescriptor = new ClassDescriptorImpl(
|
||||||
samType.getClassDescriptor().getContainingDeclaration(),
|
samType.getClassDescriptor().getContainingDeclaration(),
|
||||||
fqName.shortName(),
|
fqName.shortName(),
|
||||||
@@ -101,13 +107,18 @@ public class SamWrapperCodegen {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ClassBuilder cv = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), asmType, file);
|
ClassBuilder cv = state.getFactory().newVisitor(JvmDeclarationOriginKt.OtherOrigin(erasedInterfaceFunction), asmType, file);
|
||||||
cv.defineClass(file,
|
Type samAsmType = typeMapper.mapType(samType.getType());
|
||||||
state.getClassFileVersion(),
|
String[] superInterfaces = isKotlinFunInterface
|
||||||
ACC_FINAL | ACC_SUPER | visibility,
|
? new String[] {samAsmType.getInternalName(), FUNCTION_ADAPTER.getInternalName()}
|
||||||
asmType.getInternalName(),
|
: new String[] {samAsmType.getInternalName()};
|
||||||
null,
|
cv.defineClass(
|
||||||
OBJECT_TYPE.getInternalName(),
|
file,
|
||||||
new String[]{ typeMapper.mapType(samType.getType()).getInternalName() }
|
state.getClassFileVersion(),
|
||||||
|
ACC_FINAL | ACC_SUPER | visibility,
|
||||||
|
asmType.getInternalName(),
|
||||||
|
null,
|
||||||
|
OBJECT_TYPE.getInternalName(),
|
||||||
|
superInterfaces
|
||||||
);
|
);
|
||||||
cv.visitSource(file.getName(), null);
|
cv.visitSource(file.getName(), null);
|
||||||
|
|
||||||
@@ -126,6 +137,12 @@ public class SamWrapperCodegen {
|
|||||||
generateConstructor(asmType, functionAsmType, cv);
|
generateConstructor(asmType, functionAsmType, cv);
|
||||||
generateMethod(asmType, functionAsmType, cv, erasedInterfaceFunction, functionType);
|
generateMethod(asmType, functionAsmType, cv, erasedInterfaceFunction, functionType);
|
||||||
|
|
||||||
|
if (isKotlinFunInterface) {
|
||||||
|
generateGetFunctionDelegate(cv, asmType, functionAsmType);
|
||||||
|
generateEquals(cv, asmType, functionAsmType, samAsmType);
|
||||||
|
generateHashCode(cv, asmType, functionAsmType);
|
||||||
|
}
|
||||||
|
|
||||||
cv.done();
|
cv.done();
|
||||||
|
|
||||||
return asmType;
|
return asmType;
|
||||||
@@ -175,6 +192,62 @@ public class SamWrapperCodegen {
|
|||||||
ClosureCodegen.generateBridgesForSAM(originalInterfaceErased, erasedInterfaceFunction, codegen);
|
ClosureCodegen.generateBridgesForSAM(originalInterfaceErased, erasedInterfaceFunction, codegen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void generateEquals(
|
||||||
|
@NotNull ClassBuilder cv, @NotNull Type asmType, @NotNull Type functionAsmType, @NotNull Type samAsmType
|
||||||
|
) {
|
||||||
|
MethodVisitor mv = cv.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
|
||||||
|
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||||
|
|
||||||
|
Label notEqual = new Label();
|
||||||
|
iv.load(1, OBJECT_TYPE);
|
||||||
|
iv.instanceOf(samAsmType);
|
||||||
|
iv.ifeq(notEqual);
|
||||||
|
iv.load(1, OBJECT_TYPE);
|
||||||
|
iv.instanceOf(FUNCTION_ADAPTER);
|
||||||
|
iv.ifeq(notEqual);
|
||||||
|
|
||||||
|
iv.load(0, OBJECT_TYPE);
|
||||||
|
iv.getfield(asmType.getInternalName(), FUNCTION_FIELD_NAME, functionAsmType.getDescriptor());
|
||||||
|
iv.load(1, OBJECT_TYPE);
|
||||||
|
iv.checkcast(FUNCTION_ADAPTER);
|
||||||
|
iv.invokeinterface(FUNCTION_ADAPTER.getInternalName(), GET_FUNCTION_DELEGATE.getName(), GET_FUNCTION_DELEGATE.getDescriptor());
|
||||||
|
genAreEqualCall(iv);
|
||||||
|
iv.ifeq(notEqual);
|
||||||
|
|
||||||
|
iv.iconst(1);
|
||||||
|
Label exit = new Label();
|
||||||
|
iv.goTo(exit);
|
||||||
|
|
||||||
|
iv.visitLabel(notEqual);
|
||||||
|
iv.iconst(0);
|
||||||
|
|
||||||
|
iv.visitLabel(exit);
|
||||||
|
iv.areturn(Type.BOOLEAN_TYPE);
|
||||||
|
FunctionCodegen.endVisit(iv, "equals of SAM wrapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateHashCode(@NotNull ClassBuilder cv, @NotNull Type asmType, @NotNull Type functionAsmType) {
|
||||||
|
MethodVisitor mv = cv.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC, "hashCode", "()I", null, null);
|
||||||
|
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||||
|
iv.load(0, OBJECT_TYPE);
|
||||||
|
iv.getfield(asmType.getInternalName(), FUNCTION_FIELD_NAME, functionAsmType.getDescriptor());
|
||||||
|
iv.invokevirtual(OBJECT_TYPE.getInternalName(), "hashCode", "()I", false);
|
||||||
|
iv.areturn(Type.INT_TYPE);
|
||||||
|
FunctionCodegen.endVisit(iv, "hashCode of SAM wrapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateGetFunctionDelegate(@NotNull ClassBuilder cv, @NotNull Type asmType, @NotNull Type functionAsmType) {
|
||||||
|
MethodVisitor mv = cv.newMethod(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN, ACC_PUBLIC,
|
||||||
|
GET_FUNCTION_DELEGATE.getName(), GET_FUNCTION_DELEGATE.getDescriptor(), null, null
|
||||||
|
);
|
||||||
|
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||||
|
iv.load(0, asmType);
|
||||||
|
iv.getfield(asmType.getInternalName(), FUNCTION_FIELD_NAME, functionAsmType.getDescriptor());
|
||||||
|
iv.areturn(OBJECT_TYPE);
|
||||||
|
FunctionCodegen.endVisit(iv, "getFunctionDelegate of SAM wrapper");
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private FqName getWrapperName(
|
private FqName getWrapperName(
|
||||||
@NotNull KtFile containingFile,
|
@NotNull KtFile containingFile,
|
||||||
|
|||||||
Generated
+76
@@ -11141,6 +11141,44 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractFirBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -26697,6 +26735,44 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractFirBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class AsmTypes {
|
|||||||
public static final Type UNIT_TYPE = Type.getObjectType("kotlin/Unit");
|
public static final Type UNIT_TYPE = Type.getObjectType("kotlin/Unit");
|
||||||
|
|
||||||
public static final Type LAMBDA = Type.getObjectType("kotlin/jvm/internal/Lambda");
|
public static final Type LAMBDA = Type.getObjectType("kotlin/jvm/internal/Lambda");
|
||||||
|
public static final Type FUNCTION_ADAPTER = Type.getObjectType("kotlin/jvm/internal/FunctionAdapter");
|
||||||
|
|
||||||
public static final Type FUNCTION_REFERENCE = Type.getObjectType("kotlin/jvm/internal/FunctionReference");
|
public static final Type FUNCTION_REFERENCE = Type.getObjectType("kotlin/jvm/internal/FunctionReference");
|
||||||
public static final Type FUNCTION_REFERENCE_IMPL = Type.getObjectType("kotlin/jvm/internal/FunctionReferenceImpl");
|
public static final Type FUNCTION_REFERENCE_IMPL = Type.getObjectType("kotlin/jvm/internal/FunctionReferenceImpl");
|
||||||
@@ -40,6 +41,7 @@ public class AsmTypes {
|
|||||||
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
|
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
|
||||||
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
|
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
|
||||||
|
|
||||||
|
public static final Type FUNCTION = Type.getObjectType("kotlin/Function");
|
||||||
public static final Type FUNCTION0 = Type.getObjectType("kotlin/jvm/functions/Function0");
|
public static final Type FUNCTION0 = Type.getObjectType("kotlin/jvm/functions/Function0");
|
||||||
public static final Type FUNCTION1 = Type.getObjectType("kotlin/jvm/functions/Function1");
|
public static final Type FUNCTION1 = Type.getObjectType("kotlin/jvm/functions/Function1");
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -163,7 +163,7 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
|
|||||||
setSourceRange(createFor)
|
setSourceRange(createFor)
|
||||||
}.apply {
|
}.apply {
|
||||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||||
superTypes += superType
|
superTypes = listOf(superType) + getAdditionalSupertypes(superType)
|
||||||
parent = enclosingContainer!!
|
parent = enclosingContainer!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
|
|||||||
isSuspend = superMethod.isSuspend
|
isSuspend = superMethod.isSuspend
|
||||||
setSourceRange(createFor)
|
setSourceRange(createFor)
|
||||||
}.apply {
|
}.apply {
|
||||||
overriddenSymbols += superMethod.symbol
|
overriddenSymbols = listOf(superMethod.symbol)
|
||||||
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
|
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
|
||||||
valueParameters = superMethod.valueParameters.map { it.copyTo(this) }
|
valueParameters = superMethod.valueParameters.map { it.copyTo(this) }
|
||||||
body = context.createIrBuilder(symbol).irBlockBody {
|
body = context.createIrBuilder(symbol).irBlockBody {
|
||||||
@@ -213,8 +213,14 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateEqualsHashCode(subclass, superType, field)
|
||||||
|
|
||||||
subclass.addFakeOverrides()
|
subclass.addFakeOverrides()
|
||||||
|
|
||||||
return subclass
|
return subclass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun getAdditionalSupertypes(supertype: IrType): List<IrType> = emptyList()
|
||||||
|
|
||||||
|
protected open fun generateEqualsHashCode(klass: IrClass, supertype: IrType, functionDelegateField: IrField) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -499,6 +499,10 @@ class JvmSymbols(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val functionAdapter: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.FunctionAdapter"), ClassKind.INTERFACE) { klass ->
|
||||||
|
klass.addFunction("getFunctionDelegate", irBuiltIns.functionClass.starProjectedType, Modality.ABSTRACT)
|
||||||
|
}
|
||||||
|
|
||||||
val getOrCreateKotlinPackage: IrSimpleFunctionSymbol =
|
val getOrCreateKotlinPackage: IrSimpleFunctionSymbol =
|
||||||
reflection.functionByName("getOrCreateKotlinPackage")
|
reflection.functionByName("getOrCreateKotlinPackage")
|
||||||
|
|
||||||
|
|||||||
+57
-6
@@ -152,6 +152,11 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val needToGenerateSamEqualsHashCodeMethods =
|
||||||
|
samSuperType != null &&
|
||||||
|
samSuperType.getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB &&
|
||||||
|
(adaptedReferenceOriginalTarget != null || !isLambda)
|
||||||
|
|
||||||
private val superType =
|
private val superType =
|
||||||
samSuperType ?: when {
|
samSuperType ?: when {
|
||||||
adaptedReferenceOriginalTarget != null -> context.ir.symbols.adaptedFunctionReference
|
adaptedReferenceOriginalTarget != null -> context.ir.symbols.adaptedFunctionReference
|
||||||
@@ -169,10 +174,18 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
name = SpecialNames.NO_NAME_PROVIDED
|
name = SpecialNames.NO_NAME_PROVIDED
|
||||||
}.apply {
|
}.apply {
|
||||||
parent = currentDeclarationParent ?: error("No current declaration parent at ${irFunctionReference.dump()}")
|
parent = currentDeclarationParent ?: error("No current declaration parent at ${irFunctionReference.dump()}")
|
||||||
superTypes += superType
|
superTypes = listOfNotNull(
|
||||||
if (samSuperType == null)
|
superType,
|
||||||
superTypes += functionSuperClass.typeWith(parameterTypes)
|
if (samSuperType == null)
|
||||||
if (irFunctionReference.isSuspend) superTypes += context.ir.symbols.suspendFunctionInterface.defaultType
|
functionSuperClass.typeWith(parameterTypes)
|
||||||
|
else null,
|
||||||
|
if (irFunctionReference.isSuspend)
|
||||||
|
context.ir.symbols.suspendFunctionInterface.defaultType
|
||||||
|
else null,
|
||||||
|
if (needToGenerateSamEqualsHashCodeMethods)
|
||||||
|
context.ir.symbols.functionAdapter.defaultType
|
||||||
|
else null,
|
||||||
|
)
|
||||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||||
copyAttributes(irFunctionReference)
|
copyAttributes(irFunctionReference)
|
||||||
if (isLambda) {
|
if (isLambda) {
|
||||||
@@ -195,11 +208,11 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
fun build(): IrExpression = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
|
fun build(): IrExpression = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
|
||||||
irBlock(irFunctionReference.startOffset, irFunctionReference.endOffset) {
|
irBlock(irFunctionReference.startOffset, irFunctionReference.endOffset) {
|
||||||
val constructor = createConstructor()
|
val constructor = createConstructor()
|
||||||
createInvokeMethod(
|
val boundReceiverVar =
|
||||||
if (samSuperType != null && boundReceiver != null) {
|
if (samSuperType != null && boundReceiver != null) {
|
||||||
irTemporary(boundReceiver.second)
|
irTemporary(boundReceiver.second)
|
||||||
} else null
|
} else null
|
||||||
)
|
createInvokeMethod(boundReceiverVar)
|
||||||
|
|
||||||
if (!isLambda && samSuperType == null && !useOptimizedSuperClass) {
|
if (!isLambda && samSuperType == null && !useOptimizedSuperClass) {
|
||||||
createLegacyMethodOverride(irSymbols.functionReferenceGetSignature.owner) {
|
createLegacyMethodOverride(irSymbols.functionReferenceGetSignature.owner) {
|
||||||
@@ -213,6 +226,10 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needToGenerateSamEqualsHashCodeMethods) {
|
||||||
|
generateSamEqualsHashCodeMethods(boundReceiverVar)
|
||||||
|
}
|
||||||
|
|
||||||
+functionReferenceClass
|
+functionReferenceClass
|
||||||
+irCall(constructor.symbol).apply {
|
+irCall(constructor.symbol).apply {
|
||||||
if (valueArgumentsCount > 0) putValueArgument(0, boundReceiver!!.second)
|
if (valueArgumentsCount > 0) putValueArgument(0, boundReceiver!!.second)
|
||||||
@@ -220,6 +237,40 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun JvmIrBuilder.generateSamEqualsHashCodeMethods(boundReceiverVar: IrVariable?) {
|
||||||
|
checkNotNull(samSuperType) { "equals/hashCode can only be generated for fun interface wrappers: ${callee.render()}" }
|
||||||
|
|
||||||
|
if (!useOptimizedSuperClass) {
|
||||||
|
// This is the case of a fun interface wrapper over a (maybe adapted) function reference,
|
||||||
|
// with `-Xno-optimized-callable-referenced` enabled. We can't use constructors of FunctionReferenceImpl,
|
||||||
|
// so we'd need to basically generate a full class for a reference inheriting from FunctionReference,
|
||||||
|
// effectively disabling the optimization of fun interface wrappers over references.
|
||||||
|
// This scenario is probably not very popular because it involves using equals/hashCode on function references
|
||||||
|
// and enabling the mentioned internal compiler argument.
|
||||||
|
// Right now we generate them as abstract so that any call would result in AbstractMethodError.
|
||||||
|
// TODO: generate getFunctionDelegate, equals and hashCode properly in this case
|
||||||
|
functionReferenceClass.addFunction("equals", backendContext.irBuiltIns.booleanType, Modality.ABSTRACT).apply {
|
||||||
|
addValueParameter("other", backendContext.irBuiltIns.anyNType)
|
||||||
|
}
|
||||||
|
functionReferenceClass.addFunction("hashCode", backendContext.irBuiltIns.intType, Modality.ABSTRACT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SamEqualsHashCodeMethodsGenerator(backendContext, functionReferenceClass, samSuperType) { receiver ->
|
||||||
|
val internalClass = when {
|
||||||
|
adaptedReferenceOriginalTarget != null -> backendContext.ir.symbols.adaptedFunctionReference
|
||||||
|
else -> backendContext.ir.symbols.functionReferenceImpl
|
||||||
|
}
|
||||||
|
val constructor = internalClass.owner.constructors.single {
|
||||||
|
// arity, [receiver], owner, name, signature, flags
|
||||||
|
it.valueParameters.size == 1 + (if (boundReceiver != null) 1 else 0) + 4
|
||||||
|
}
|
||||||
|
irCallConstructor(constructor.symbol, emptyList()).apply {
|
||||||
|
generateConstructorCallArguments(this) { irGet(boundReceiverVar!!) }
|
||||||
|
}
|
||||||
|
}.generate()
|
||||||
|
}
|
||||||
|
|
||||||
private fun createConstructor(): IrConstructor =
|
private fun createConstructor(): IrConstructor =
|
||||||
functionReferenceClass.addConstructor {
|
functionReferenceClass.addConstructor {
|
||||||
origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE
|
origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE
|
||||||
|
|||||||
+120
-1
@@ -6,12 +6,24 @@
|
|||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.SingleAbstractMethodLowering
|
import org.jetbrains.kotlin.backend.common.lower.SingleAbstractMethodLowering
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
|
import org.jetbrains.kotlin.ir.types.typeWith
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
import org.jetbrains.kotlin.load.java.JavaVisibilities
|
||||||
|
|
||||||
internal val singleAbstractMethodPhase = makeIrFilePhase(
|
internal val singleAbstractMethodPhase = makeIrFilePhase(
|
||||||
@@ -21,8 +33,115 @@ internal val singleAbstractMethodPhase = makeIrFilePhase(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : SingleAbstractMethodLowering(context) {
|
private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : SingleAbstractMethodLowering(context) {
|
||||||
|
private val jvmContext: JvmBackendContext get() = context as JvmBackendContext
|
||||||
|
|
||||||
override val privateGeneratedWrapperVisibility: Visibility
|
override val privateGeneratedWrapperVisibility: Visibility
|
||||||
get() = JavaVisibilities.PACKAGE_VISIBILITY
|
get() = JavaVisibilities.PACKAGE_VISIBILITY
|
||||||
|
|
||||||
override fun getSuperTypeForWrapper(typeOperand: IrType) = typeOperand.erasedUpperBound.defaultType
|
override fun getSuperTypeForWrapper(typeOperand: IrType): IrType =
|
||||||
|
typeOperand.erasedUpperBound.defaultType
|
||||||
|
|
||||||
|
private val IrType.isKotlinFunInterface: Boolean
|
||||||
|
get() = getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
|
||||||
|
|
||||||
|
override fun getAdditionalSupertypes(supertype: IrType): List<IrType> =
|
||||||
|
if (supertype.isKotlinFunInterface) listOf(jvmContext.ir.symbols.functionAdapter.owner.typeWith())
|
||||||
|
else emptyList()
|
||||||
|
|
||||||
|
override fun generateEqualsHashCode(klass: IrClass, supertype: IrType, functionDelegateField: IrField) {
|
||||||
|
if (!supertype.isKotlinFunInterface) return
|
||||||
|
|
||||||
|
SamEqualsHashCodeMethodsGenerator(jvmContext, klass, supertype) { receiver ->
|
||||||
|
irGetField(receiver, functionDelegateField)
|
||||||
|
}.generate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates equals and hashCode for SAM and fun interface wrappers, as well as an implementation of getFunctionDelegate
|
||||||
|
* (inherited from kotlin.jvm.internal.FunctionAdapter), needed to properly implement them.
|
||||||
|
* This class is used in two places:
|
||||||
|
* - FunctionReferenceLowering, which is the case of SAM conversion of a (maybe adapted) function reference, e.g. `FunInterface(foo::bar)`.
|
||||||
|
* Note that we don't generate equals/hashCode for SAM conversion of lambdas, e.g. `FunInterface {}`, even though lambdas are represented
|
||||||
|
* as a local function + reference to it. The reason for this is that all lambdas are unique, so after SAM conversion they are still
|
||||||
|
* never equal to each other. See [FunctionReferenceLowering.FunctionReferenceBuilder.needToGenerateSamEqualsHashCodeMethods].
|
||||||
|
* - JvmSingleAbstractMethodLowering, which is the case of SAM conversion of any value of a functional type,
|
||||||
|
* e.g. `val f = {}; FunInterface(f)`.
|
||||||
|
*/
|
||||||
|
internal class SamEqualsHashCodeMethodsGenerator(
|
||||||
|
private val context: JvmBackendContext,
|
||||||
|
private val klass: IrClass,
|
||||||
|
private val samSuperType: IrType,
|
||||||
|
private val obtainFunctionDelegate: IrBuilderWithScope.(receiver: IrExpression) -> IrExpression,
|
||||||
|
) {
|
||||||
|
private val builtIns: IrBuiltIns get() = context.irBuiltIns
|
||||||
|
private val functionAdapterClass = context.ir.symbols.functionAdapter.owner
|
||||||
|
private val getFunctionDelegate = functionAdapterClass.functions.single { it.name.asString() == "getFunctionDelegate" }
|
||||||
|
|
||||||
|
fun generate() {
|
||||||
|
generateGetFunctionDelegate()
|
||||||
|
generateEquals()
|
||||||
|
generateHashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateGetFunctionDelegate() {
|
||||||
|
klass.addFunction(getFunctionDelegate.name.asString(), getFunctionDelegate.returnType).apply {
|
||||||
|
overriddenSymbols = listOf(getFunctionDelegate.symbol)
|
||||||
|
body = context.createIrBuilder(symbol).run {
|
||||||
|
irExprBody(obtainFunctionDelegate(irGet(dispatchReceiverParameter!!)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateEquals() {
|
||||||
|
klass.addFunction("equals", builtIns.booleanType).apply {
|
||||||
|
overriddenSymbols = listOf(samSuperType.getClass()!!.functions.single {
|
||||||
|
it.name.asString() == "equals" &&
|
||||||
|
it.extensionReceiverParameter == null &&
|
||||||
|
it.valueParameters.singleOrNull()?.type == builtIns.anyNType
|
||||||
|
}.symbol)
|
||||||
|
|
||||||
|
val other = addValueParameter("other", builtIns.anyNType)
|
||||||
|
body = context.createIrBuilder(symbol).run {
|
||||||
|
irExprBody(
|
||||||
|
irIfThenElse(
|
||||||
|
builtIns.booleanType,
|
||||||
|
irIs(irGet(other), samSuperType),
|
||||||
|
irIfThenElse(
|
||||||
|
builtIns.booleanType,
|
||||||
|
irIs(irGet(other), functionAdapterClass.typeWith()),
|
||||||
|
irEquals(
|
||||||
|
irCall(getFunctionDelegate).also {
|
||||||
|
it.dispatchReceiver = irGet(dispatchReceiverParameter!!)
|
||||||
|
},
|
||||||
|
irCall(getFunctionDelegate).also {
|
||||||
|
it.dispatchReceiver = irImplicitCast(irGet(other), functionAdapterClass.typeWith())
|
||||||
|
}
|
||||||
|
),
|
||||||
|
irFalse()
|
||||||
|
),
|
||||||
|
irFalse()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateHashCode() {
|
||||||
|
klass.addFunction("hashCode", builtIns.intType).apply {
|
||||||
|
val hashCode = klass.superTypes.first().getClass()!!.functions.single {
|
||||||
|
it.name.asString() == "hashCode" && it.extensionReceiverParameter == null && it.valueParameters.isEmpty()
|
||||||
|
}.symbol
|
||||||
|
overriddenSymbols = listOf(hashCode)
|
||||||
|
body = context.createIrBuilder(symbol).run {
|
||||||
|
irExprBody(
|
||||||
|
irCall(hashCode).also {
|
||||||
|
it.dispatchReceiver = irCall(getFunctionDelegate).also {
|
||||||
|
it.dispatchReceiver = irGet(dispatchReceiverParameter!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
// IGNORE_BACKEND: JS, JS_IR, NATIVE
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkEqual(x: Any, y: Any) {
|
||||||
|
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
|
||||||
|
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FunInterface {
|
||||||
|
fun invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
class C {
|
||||||
|
fun target1() {}
|
||||||
|
fun target2() {}
|
||||||
|
|
||||||
|
fun adapted1(s: String? = null): String = s!!
|
||||||
|
fun adapted2(vararg s: String): String = s[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val c0 = C()
|
||||||
|
|
||||||
|
checkEqual(id(c0::target1), id(c0::target1))
|
||||||
|
checkEqual(id(c0::target1), target1FromOtherFile(c0))
|
||||||
|
|
||||||
|
checkNotEqual(id(c0::target1), id(c0::target2))
|
||||||
|
|
||||||
|
checkEqual(id(c0::adapted1), id(c0::adapted1))
|
||||||
|
checkEqual(id(c0::adapted1), adapted1FromOtherFile(c0))
|
||||||
|
checkEqual(id(c0::adapted2), id(c0::adapted2))
|
||||||
|
checkEqual(id(c0::adapted2), adapted2FromOtherFile(c0))
|
||||||
|
checkNotEqual(id(c0::adapted1), id(c0::adapted2))
|
||||||
|
|
||||||
|
val c1 = C()
|
||||||
|
checkNotEqual(id(c0::target1), id(c1::target1))
|
||||||
|
checkNotEqual(id(c0::target1), id(c1::target2))
|
||||||
|
checkNotEqual(id(c0::adapted1), id(c1::adapted1))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun target1FromOtherFile(c0: C): Any = id(c0::target1)
|
||||||
|
fun adapted1FromOtherFile(c0: C): Any = id(c0::adapted1)
|
||||||
|
fun adapted2FromOtherFile(c0: C): Any = id(c0::adapted2)
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
// IGNORE_BACKEND: JS, JS_IR, NATIVE
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkEqual(x: Any, y: Any) {
|
||||||
|
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
|
||||||
|
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FunInterface {
|
||||||
|
fun invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun target1() {}
|
||||||
|
fun target2() {}
|
||||||
|
|
||||||
|
fun adapted1(s: String? = null): String = s!!
|
||||||
|
fun adapted2(vararg s: String): String = s[0]
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
checkEqual(id(::target1), id(::target1))
|
||||||
|
checkEqual(id(::target1), target1FromOtherFile())
|
||||||
|
|
||||||
|
checkNotEqual(id(::target1), id(::target2))
|
||||||
|
|
||||||
|
checkEqual(id(::adapted1), id(::adapted1))
|
||||||
|
checkEqual(id(::adapted1), adapted1FromOtherFile())
|
||||||
|
checkEqual(id(::adapted2), id(::adapted2))
|
||||||
|
checkEqual(id(::adapted2), adapted2FromOtherFile())
|
||||||
|
checkNotEqual(id(::adapted1), id(::adapted2))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun target1FromOtherFile(): Any = id(::target1)
|
||||||
|
fun adapted1FromOtherFile(): Any = id(::adapted1)
|
||||||
|
fun adapted2FromOtherFile(): Any = id(::adapted2)
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// IGNORE_BACKEND: JS, JS_IR, NATIVE
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkEqual(x: Any, y: Any) {
|
||||||
|
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
|
||||||
|
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FunInterface {
|
||||||
|
fun invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
val lambda = {}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
checkEqual(id(lambda), id(lambda))
|
||||||
|
checkEqual(id(lambda), lambdaFromOtherFile())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun lambdaFromOtherFile(): Any = id(lambda)
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
// IGNORE_BACKEND: JS, JS_IR, NATIVE
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
|
||||||
|
fun checkEqual(x: Any, y: Any) {
|
||||||
|
if (x != y || y != x) throw AssertionError("$x and $y should be equal")
|
||||||
|
if (x.hashCode() != y.hashCode()) throw AssertionError("$x and $y should have the same hash code")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interface FunInterface {
|
||||||
|
fun invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
fun local1() {}
|
||||||
|
fun local2() {}
|
||||||
|
|
||||||
|
checkEqual(id(::local1), id(::local1))
|
||||||
|
checkNotEqual(id(::local1), id(::local2))
|
||||||
|
|
||||||
|
fun String.localExt() {}
|
||||||
|
|
||||||
|
checkEqual(id("A"::localExt), id("A"::localExt))
|
||||||
|
checkNotEqual(id("A"::localExt), id("B"::localExt))
|
||||||
|
|
||||||
|
fun adapted(default: String? = "", vararg va: Int): Int = 0
|
||||||
|
|
||||||
|
checkEqual(id(::adapted), id(::adapted))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// IGNORE_BACKEND: JS_IR
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
|
||||||
|
fun interface FunInterface {
|
||||||
|
fun invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: FunInterface): Any = f
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (id { "lambda" } == id { "lambda" }) return "Fail: SAMs over lambdas are never equal"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
class C {
|
||||||
|
fun target1() {}
|
||||||
|
fun target2() {}
|
||||||
|
|
||||||
|
fun adapted1(s: String? = null): String = s!!
|
||||||
|
fun adapted2(vararg s: String): String = s[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
// Since 1.0, SAM wrappers for Java do not implement equals/hashCode
|
||||||
|
val c0 = C()
|
||||||
|
|
||||||
|
checkNotEqual(id(c0::target1), id(c0::target1))
|
||||||
|
checkNotEqual(id(c0::target1), target1FromOtherFile(c0))
|
||||||
|
checkNotEqual(id(c0::target1), id(c0::target2))
|
||||||
|
|
||||||
|
checkNotEqual(id(c0::adapted1), id(c0::adapted1))
|
||||||
|
checkNotEqual(id(c0::adapted1), adapted1FromOtherFile(c0))
|
||||||
|
checkNotEqual(id(c0::adapted2), id(c0::adapted2))
|
||||||
|
checkNotEqual(id(c0::adapted2), adapted2FromOtherFile(c0))
|
||||||
|
checkNotEqual(id(c0::adapted1), id(c0::adapted2))
|
||||||
|
|
||||||
|
val c1 = C()
|
||||||
|
checkNotEqual(id(c0::target1), id(c1::target1))
|
||||||
|
checkNotEqual(id(c0::target1), id(c1::target2))
|
||||||
|
checkNotEqual(id(c0::adapted1), id(c1::adapted1))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun target1FromOtherFile(c0: C): Any = id(c0::target1)
|
||||||
|
fun adapted1FromOtherFile(c0: C): Any = id(c0::adapted1)
|
||||||
|
fun adapted2FromOtherFile(c0: C): Any = id(c0::adapted2)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun target1() {}
|
||||||
|
fun target2() {}
|
||||||
|
|
||||||
|
fun adapted1(s: String? = null): String = s!!
|
||||||
|
fun adapted2(vararg s: String): String = s[0]
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
// Since 1.0, SAM wrappers for Java do not implement equals/hashCode
|
||||||
|
checkNotEqual(id(::target1), id(::target1))
|
||||||
|
checkNotEqual(id(::target1), target1FromOtherFile())
|
||||||
|
checkNotEqual(id(::target1), id(::target2))
|
||||||
|
|
||||||
|
checkNotEqual(id(::adapted1), id(::adapted1))
|
||||||
|
checkNotEqual(id(::adapted1), adapted1FromOtherFile())
|
||||||
|
checkNotEqual(id(::adapted2), id(::adapted2))
|
||||||
|
checkNotEqual(id(::adapted2), adapted2FromOtherFile())
|
||||||
|
checkNotEqual(id(::adapted1), id(::adapted2))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun target1FromOtherFile(): Any = id(::target1)
|
||||||
|
fun adapted1FromOtherFile(): Any = id(::adapted1)
|
||||||
|
fun adapted2FromOtherFile(): Any = id(::adapted2)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
val lambda = {}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
// Since 1.0, SAM wrappers for Java do not implement equals/hashCode
|
||||||
|
checkNotEqual(id(lambda), id(lambda))
|
||||||
|
checkNotEqual(id(lambda), lambdaFromOtherFile())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: fromOtherFile.kt
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun lambdaFromOtherFile(): Any = id(lambda)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
|
||||||
|
fun checkNotEqual(x: Any, y: Any) {
|
||||||
|
if (x == y || y == x) throw AssertionError("$x and $y should NOT be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
// Since 1.0, SAM wrappers for Java do not implement equals/hashCode
|
||||||
|
|
||||||
|
fun local1() {}
|
||||||
|
fun local2() {}
|
||||||
|
|
||||||
|
checkNotEqual(id(::local1), id(::local1))
|
||||||
|
checkNotEqual(id(::local1), id(::local2))
|
||||||
|
|
||||||
|
fun String.localExt() {}
|
||||||
|
|
||||||
|
checkNotEqual(id("A"::localExt), id("A"::localExt))
|
||||||
|
checkNotEqual(id("A"::localExt), id("B"::localExt))
|
||||||
|
|
||||||
|
fun adapted(default: String? = "", vararg va: Int): Int = 0
|
||||||
|
|
||||||
|
checkNotEqual(id(::adapted), id(::adapted))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// TARGET_BACKEND: JVM
|
||||||
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
|
|
||||||
|
private fun id(f: Runnable): Any = f
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
if (id { "lambda" } == id { "lambda" }) return "Fail: SAMs over lambdas are never equal"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+76
@@ -12356,6 +12356,44 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -28283,6 +28321,44 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
+76
@@ -12356,6 +12356,44 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractLightAnalysisModeTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -27100,6 +27138,44 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractLightAnalysisModeTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
+76
@@ -11141,6 +11141,44 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractIrBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -26697,6 +26735,44 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/syntheticVsReal.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractIrBlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
Generated
+51
@@ -9551,6 +9551,44 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractIrJsCodegenBoxTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -21573,6 +21611,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractIrJsCodegenBoxTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
+51
@@ -9551,6 +9551,44 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
public void testSuspendFunInterfaceConversionCodegen() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
runTest("compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/funInterface/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractJsCodegenBoxTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/funInterface/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesBound.kt")
|
||||||
|
public void testFunctionReferencesBound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesBound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionReferencesUnbound.kt")
|
||||||
|
public void testFunctionReferencesUnbound() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/functionReferencesUnbound.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaRuntimeConversion.kt")
|
||||||
|
public void testLambdaRuntimeConversion() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/lambdaRuntimeConversion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("localFunctionReferences.kt")
|
||||||
|
public void testLocalFunctionReferences() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/localFunctionReferences.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleLambdas.kt")
|
||||||
|
public void testSimpleLambdas() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/funInterface/equality/simpleLambdas.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/functions")
|
@TestMetadata("compiler/testData/codegen/box/functions")
|
||||||
@@ -21633,6 +21671,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt");
|
runTest("compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/sam/equality")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Equality extends AbstractJsCodegenBoxTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInEquality() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/sealed")
|
@TestMetadata("compiler/testData/codegen/box/sealed")
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.jvm.internal;
|
||||||
|
|
||||||
|
import kotlin.Function;
|
||||||
|
import kotlin.SinceKotlin;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@SinceKotlin(version = "1.4")
|
||||||
|
public interface FunctionAdapter {
|
||||||
|
Function<?> getFunctionDelegate();
|
||||||
|
}
|
||||||
+4
@@ -3451,6 +3451,10 @@ public final class kotlin/jvm/internal/FloatSpreadBuilder : kotlin/jvm/internal/
|
|||||||
public final fun toArray ()[F
|
public final fun toArray ()[F
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract interface class kotlin/jvm/internal/FunctionAdapter {
|
||||||
|
public abstract fun getFunctionDelegate ()Lkotlin/Function;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract interface class kotlin/jvm/internal/FunctionBase : kotlin/Function {
|
public abstract interface class kotlin/jvm/internal/FunctionBase : kotlin/Function {
|
||||||
public abstract fun getArity ()I
|
public abstract fun getArity ()I
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user