Fix exception from data class codegen for light classes
EA-66827
This commit is contained in:
@@ -19,11 +19,9 @@ package org.jetbrains.kotlin.backend.common;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.bridges.BridgesPackage;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
@@ -41,19 +39,11 @@ import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
|
||||
|
||||
/**
|
||||
* Backend-independent utility class.
|
||||
*/
|
||||
public class CodegenUtil {
|
||||
|
||||
private CodegenUtil() {
|
||||
}
|
||||
|
||||
// TODO: consider putting predefined method signatures here too.
|
||||
public static final String EQUALS_METHOD_NAME = "equals";
|
||||
public static final String TO_STRING_METHOD_NAME = "toString";
|
||||
public static final String HASH_CODE_METHOD_NAME = "hashCode";
|
||||
|
||||
@Nullable
|
||||
public static FunctionDescriptor getDeclaredFunctionByRawSignature(
|
||||
@NotNull ClassDescriptor owner,
|
||||
@@ -73,29 +63,6 @@ public class CodegenUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FunctionDescriptor getAnyEqualsMethod(@NotNull KotlinBuiltIns builtIns) {
|
||||
ClassDescriptor anyClass = builtIns.getAny();
|
||||
FunctionDescriptor function = getDeclaredFunctionByRawSignature(
|
||||
anyClass, Name.identifier(EQUALS_METHOD_NAME), builtIns.getBoolean(), anyClass
|
||||
);
|
||||
assert function != null;
|
||||
return function;
|
||||
}
|
||||
|
||||
public static FunctionDescriptor getAnyToStringMethod(@NotNull KotlinBuiltIns builtIns) {
|
||||
ClassDescriptor anyClass = builtIns.getAny();
|
||||
FunctionDescriptor function = getDeclaredFunctionByRawSignature(anyClass, Name.identifier(TO_STRING_METHOD_NAME), builtIns.getString());
|
||||
assert function != null;
|
||||
return function;
|
||||
}
|
||||
|
||||
public static FunctionDescriptor getAnyHashCodeMethod(@NotNull KotlinBuiltIns builtIns) {
|
||||
ClassDescriptor anyClass = builtIns.getAny();
|
||||
FunctionDescriptor function = getDeclaredFunctionByRawSignature(anyClass, Name.identifier(HASH_CODE_METHOD_NAME), builtIns.getInt());
|
||||
assert function != null;
|
||||
return function;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression, ClassDescriptor classDescriptor, BindingContext bindingContext) {
|
||||
PropertyDescriptor propertyDescriptor = null;
|
||||
|
||||
+34
-35
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.backend.common;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.JetClass;
|
||||
@@ -41,11 +43,13 @@ public abstract class DataClassMethodGenerator {
|
||||
private final JetClassOrObject declaration;
|
||||
private final BindingContext bindingContext;
|
||||
private final ClassDescriptor classDescriptor;
|
||||
private final KotlinBuiltIns builtIns;
|
||||
|
||||
public DataClassMethodGenerator(JetClassOrObject declaration, BindingContext bindingContext) {
|
||||
this.declaration = declaration;
|
||||
this.bindingContext = bindingContext;
|
||||
this.classDescriptor = BindingContextUtils.getNotNull(bindingContext, BindingContext.CLASS, declaration);
|
||||
this.builtIns = getBuiltIns(classDescriptor);
|
||||
}
|
||||
|
||||
public void generate() {
|
||||
@@ -61,20 +65,17 @@ public abstract class DataClassMethodGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Backend-specific implementations.
|
||||
protected abstract void generateComponentFunction(
|
||||
@NotNull FunctionDescriptor function,
|
||||
@NotNull ValueParameterDescriptor parameter
|
||||
);
|
||||
protected abstract void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter);
|
||||
|
||||
protected abstract void generateCopyFunction(@NotNull FunctionDescriptor function, @NotNull List<JetParameter> constructorParameters);
|
||||
|
||||
protected abstract void generateToStringMethod(@NotNull List<PropertyDescriptor> properties);
|
||||
protected abstract void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties);
|
||||
|
||||
protected abstract void generateHashCodeMethod(@NotNull List<PropertyDescriptor> properties);
|
||||
protected abstract void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties);
|
||||
|
||||
protected abstract void generateEqualsMethod(@NotNull List<PropertyDescriptor> properties);
|
||||
protected abstract void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties);
|
||||
|
||||
@NotNull
|
||||
protected ClassDescriptor getClassDescriptor() {
|
||||
return classDescriptor;
|
||||
}
|
||||
@@ -101,24 +102,23 @@ public abstract class DataClassMethodGenerator {
|
||||
}
|
||||
|
||||
private void generateDataClassToStringIfNeeded(@NotNull List<PropertyDescriptor> properties) {
|
||||
ClassDescriptor stringClass = getBuiltIns(classDescriptor).getString();
|
||||
if (!hasDeclaredNonTrivialMember(CodegenUtil.TO_STRING_METHOD_NAME, stringClass)) {
|
||||
generateToStringMethod(properties);
|
||||
FunctionDescriptor function = getDeclaredMember("toString", builtIns.getString());
|
||||
if (function != null && isTrivial(function)) {
|
||||
generateToStringMethod(function, properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDataClassHashCodeIfNeeded(@NotNull List<PropertyDescriptor> properties) {
|
||||
ClassDescriptor intClass = getBuiltIns(classDescriptor).getInt();
|
||||
if (!hasDeclaredNonTrivialMember(CodegenUtil.HASH_CODE_METHOD_NAME, intClass)) {
|
||||
generateHashCodeMethod(properties);
|
||||
FunctionDescriptor function = getDeclaredMember("hashCode", builtIns.getInt());
|
||||
if (function != null && isTrivial(function)) {
|
||||
generateHashCodeMethod(function, properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDataClassEqualsIfNeeded(@NotNull List<PropertyDescriptor> properties) {
|
||||
ClassDescriptor booleanClass = getBuiltIns(classDescriptor).getBoolean();
|
||||
ClassDescriptor anyClass = getBuiltIns(classDescriptor).getAny();
|
||||
if (!hasDeclaredNonTrivialMember(CodegenUtil.EQUALS_METHOD_NAME, booleanClass, anyClass)) {
|
||||
generateEqualsMethod(properties);
|
||||
FunctionDescriptor function = getDeclaredMember("equals", builtIns.getBoolean(), builtIns.getAny());
|
||||
if (function != null && isTrivial(function)) {
|
||||
generateEqualsMethod(function, properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,42 +132,41 @@ public abstract class DataClassMethodGenerator {
|
||||
return result;
|
||||
}
|
||||
|
||||
private
|
||||
@NotNull
|
||||
List<JetParameter> getPrimaryConstructorParameters() {
|
||||
private List<JetParameter> getPrimaryConstructorParameters() {
|
||||
if (declaration instanceof JetClass) {
|
||||
return ((JetClass) declaration).getPrimaryConstructorParameters();
|
||||
return declaration.getPrimaryConstructorParameters();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the class has a declared member with the given name anywhere in its hierarchy besides Any
|
||||
*/
|
||||
private boolean hasDeclaredNonTrivialMember(
|
||||
@Nullable
|
||||
private FunctionDescriptor getDeclaredMember(
|
||||
@NotNull String name,
|
||||
@NotNull ClassDescriptor returnedClassifier,
|
||||
@NotNull ClassDescriptor... valueParameterClassifiers
|
||||
) {
|
||||
FunctionDescriptor function =
|
||||
CodegenUtil.getDeclaredFunctionByRawSignature(classDescriptor, Name.identifier(name), returnedClassifier,
|
||||
valueParameterClassifiers);
|
||||
if (function == null) {
|
||||
return false;
|
||||
}
|
||||
return CodegenUtil.getDeclaredFunctionByRawSignature(
|
||||
classDescriptor, Name.identifier(name), returnedClassifier, valueParameterClassifiers
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the member is an inherited implementation of a method from Any
|
||||
*/
|
||||
private boolean isTrivial(@NotNull FunctionDescriptor function) {
|
||||
if (function.getKind() == CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (CallableDescriptor overridden : OverrideResolver.getOverriddenDeclarations(function)) {
|
||||
if (overridden instanceof CallableMemberDescriptor
|
||||
&& ((CallableMemberDescriptor) overridden).getKind() == CallableMemberDescriptor.Kind.DECLARATION
|
||||
&& !overridden.getContainingDeclaration().equals(getBuiltIns(classDescriptor).getAny())) {
|
||||
return true;
|
||||
&& !overridden.getContainingDeclaration().equals(builtIns.getAny())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,17 +484,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateEqualsMethod(@NotNull List<PropertyDescriptor> properties) {
|
||||
KotlinBuiltIns builtins = getBuiltIns(descriptor);
|
||||
FunctionDescriptor equalsFunction = CodegenUtil.getDeclaredFunctionByRawSignature(
|
||||
descriptor, Name.identifier(CodegenUtil.EQUALS_METHOD_NAME), builtins.getBoolean(), builtins.getAny()
|
||||
);
|
||||
|
||||
assert equalsFunction != null : String.format("Should be called only for classes with non-trivial '%s'. In %s, %s",
|
||||
CodegenUtil.EQUALS_METHOD_NAME, descriptor.getName(), descriptor);
|
||||
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(equalsFunction);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(equalsFunction), ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
|
||||
public void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties) {
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(function);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(function), ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
mv.visitCode();
|
||||
@@ -560,16 +552,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateHashCodeMethod(@NotNull List<PropertyDescriptor> properties) {
|
||||
FunctionDescriptor hashCodeFunction = CodegenUtil.getDeclaredFunctionByRawSignature(
|
||||
descriptor, Name.identifier(CodegenUtil.HASH_CODE_METHOD_NAME), getBuiltIns(descriptor).getInt()
|
||||
);
|
||||
|
||||
assert hashCodeFunction != null : String.format("Should be called only for classes with non-trivial '%s'. In %s, %s",
|
||||
CodegenUtil.HASH_CODE_METHOD_NAME, descriptor.getName(), descriptor);
|
||||
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(hashCodeFunction);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(hashCodeFunction), ACC_PUBLIC, "hashCode", "()I", null, null);
|
||||
public void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties) {
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(function);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(function), ACC_PUBLIC, "hashCode", "()I", null, null);
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
mv.visitCode();
|
||||
@@ -616,16 +601,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateToStringMethod(@NotNull List<PropertyDescriptor> properties) {
|
||||
FunctionDescriptor toString = CodegenUtil.getDeclaredFunctionByRawSignature(
|
||||
descriptor, Name.identifier(CodegenUtil.TO_STRING_METHOD_NAME), getBuiltIns(descriptor).getString()
|
||||
);
|
||||
|
||||
assert toString != null : String.format("Should be called only for classes with non-trivial '%s'. In %s, %s",
|
||||
CodegenUtil.TO_STRING_METHOD_NAME, descriptor.getName(), descriptor);
|
||||
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(toString);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(toString), ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
|
||||
public void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> properties) {
|
||||
MethodContext context = ImplementationBodyCodegen.this.context.intoFunction(function);
|
||||
MethodVisitor mv = v.newMethod(OtherOrigin(function), ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
data class A(val x: Int) {
|
||||
fun toArray(): IntArray =
|
||||
intArrayOf(x)
|
||||
|
||||
override fun <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>toString<!>() =
|
||||
toArray().takeWhile { it != -1 } // .joinToString()
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
kotlin.data() public final class A {
|
||||
public constructor A(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final /*synthesized*/ fun component1(): kotlin.Int
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ...): A
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final fun toArray(): kotlin.IntArray
|
||||
public open override /*1*/ fun toString(): kotlin.List<kotlin.Int>
|
||||
}
|
||||
+6
@@ -763,6 +763,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea66827_dataClassWrongToString.kt")
|
||||
public void testEa66827_dataClassWrongToString() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/regression/ea66827_dataClassWrongToString.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ea70880_illegalJvmName.kt")
|
||||
public void testEa70880_illegalJvmName() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/regression/ea70880_illegalJvmName.kt");
|
||||
|
||||
+6
-13
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.declaration;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.backend.common.DataClassMethodGenerator;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
@@ -31,9 +30,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static kotlin.KotlinPackage.first;
|
||||
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns;
|
||||
|
||||
class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||
private final TranslationContext context;
|
||||
private final List<? super JsPropertyInitializer> output;
|
||||
@@ -91,11 +87,10 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateToStringMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
public void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> classProperties) {
|
||||
// TODO: relax this limitation, with the data generation logic fixed.
|
||||
assert !classProperties.isEmpty();
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyToStringMethod(getBuiltIns(first(classProperties)));
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
|
||||
JsProgram jsProgram = context.program();
|
||||
JsExpression result = null;
|
||||
@@ -118,9 +113,8 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateHashCodeMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyHashCodeMethod(getBuiltIns(first(classProperties)));
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
public void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> classProperties) {
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
|
||||
JsProgram jsProgram = context.program();
|
||||
List<JsStatement> statements = functionObj.getBody().getStatements();
|
||||
@@ -144,10 +138,9 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateEqualsMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
public void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List<PropertyDescriptor> classProperties) {
|
||||
assert !classProperties.isEmpty();
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyEqualsMethod(getBuiltIns(first(classProperties)));
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
JsFunctionScope funScope = functionObj.getScope();
|
||||
|
||||
JsName paramName = funScope.declareName("other");
|
||||
|
||||
Reference in New Issue
Block a user