Fix exception from data class codegen for light classes

EA-66827
This commit is contained in:
Alexander Udalov
2015-09-09 18:10:51 +03:00
parent 47b8853051
commit a946f787bc
7 changed files with 74 additions and 112 deletions
@@ -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;
@@ -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();
@@ -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()
}
@@ -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>
}
@@ -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");
@@ -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");