Fail with NPE on explicit 'equals' call for null platform type value
This commit is contained in:
@@ -20,6 +20,10 @@ import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.Callable
|
||||
import org.jetbrains.kotlin.codegen.CallableMethod
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
internal val equalsMethodDescriptor: String =
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Any::class.java));
|
||||
|
||||
class Equals : IntrinsicMethod() {
|
||||
override fun toCallable(method: CallableMethod): Callable =
|
||||
@@ -32,3 +36,15 @@ class Equals : IntrinsicMethod() {
|
||||
AsmUtil.genAreEqualCall(it)
|
||||
}
|
||||
}
|
||||
|
||||
class ConsistentEquals(private val lhsType: Type) : IntrinsicMethod() {
|
||||
override fun toCallable(method: CallableMethod): Callable =
|
||||
createBinaryIntrinsicCallable(
|
||||
method.returnType,
|
||||
OBJECT_TYPE,
|
||||
nullOrObject(method.dispatchReceiverType),
|
||||
nullOrObject(method.extensionReceiverType)
|
||||
) {
|
||||
it.invokevirtual(lhsType.internalName, "equals", equalsMethodDescriptor, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.*;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
@@ -46,7 +48,6 @@ public class IntrinsicMethods {
|
||||
private static final IntrinsicMethod RANGE_TO = new RangeTo();
|
||||
private static final IntrinsicMethod INC = new Increment(1);
|
||||
private static final IntrinsicMethod DEC = new Increment(-1);
|
||||
private final IntrinsicMethod HASH_CODE;
|
||||
|
||||
private static final IntrinsicMethod ARRAY_SIZE = new ArraySize();
|
||||
private static final Equals EQUALS = new Equals();
|
||||
@@ -61,7 +62,10 @@ public class IntrinsicMethods {
|
||||
private final IntrinsicsMap intrinsicsMap = new IntrinsicsMap();
|
||||
|
||||
public IntrinsicMethods(JvmTarget jvmTarget) {
|
||||
HASH_CODE = new HashCode(jvmTarget);
|
||||
this(jvmTarget, true);
|
||||
}
|
||||
|
||||
public IntrinsicMethods(JvmTarget jvmTarget, boolean useConsistentEqualsForPrimitiveWrappers) {
|
||||
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, RECEIVER_PARAMETER_FQ_NAME, "javaClass", -1, JavaClassProperty.INSTANCE);
|
||||
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, KotlinBuiltIns.FQ_NAMES.kClass, "java", -1, new KClassJavaProperty());
|
||||
intrinsicsMap.registerIntrinsic(KotlinBuiltIns.FQ_NAMES.kCallable.toSafe(), null, "name", -1, new KCallableNameProperty());
|
||||
@@ -94,10 +98,20 @@ public class IntrinsicMethods {
|
||||
declareIntrinsicFunction(typeFqName, "dec", 0, DEC);
|
||||
}
|
||||
|
||||
IntrinsicMethod hashCode = new HashCode(jvmTarget);
|
||||
for (PrimitiveType type : PrimitiveType.values()) {
|
||||
FqName typeFqName = type.getTypeFqName();
|
||||
declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS);
|
||||
declareIntrinsicFunction(typeFqName, "hashCode", 0, HASH_CODE);
|
||||
IntrinsicMethod equalsMethod;
|
||||
if (useConsistentEqualsForPrimitiveWrappers) {
|
||||
Type wrapperType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(JvmPrimitiveType.get(type).getWrapperFqName());
|
||||
equalsMethod = new ConsistentEquals(wrapperType);
|
||||
}
|
||||
else {
|
||||
equalsMethod = EQUALS;
|
||||
}
|
||||
|
||||
declareIntrinsicFunction(typeFqName, "equals", 1, equalsMethod);
|
||||
declareIntrinsicFunction(typeFqName, "hashCode", 0, hashCode);
|
||||
declareIntrinsicFunction(typeFqName, "toString", 0, TO_STRING);
|
||||
|
||||
intrinsicsMap.registerIntrinsic(
|
||||
|
||||
@@ -126,6 +126,8 @@ class GenerationState @JvmOverloads constructor(
|
||||
extraJvmDiagnosticsTrace.bindingContext.diagnostics
|
||||
}
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
|
||||
val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT
|
||||
val isJvm8Target: Boolean = target == JvmTarget.JVM_1_8
|
||||
val isJvm8TargetWithDefaults: Boolean = isJvm8Target && configuration.getBoolean(JVMConfigurationKeys.JVM8_TARGET_WITH_DEFAULTS)
|
||||
@@ -140,7 +142,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
this.bindingContext, classBuilderMode, IncompatibleClassTrackerImpl(extraJvmDiagnosticsTrace),
|
||||
this.moduleName, isJvm8Target, isJvm8TargetWithDefaults
|
||||
)
|
||||
val intrinsics: IntrinsicMethods = IntrinsicMethods(target)
|
||||
val intrinsics: IntrinsicMethods = IntrinsicMethods(target, languageVersionSettings.supportsFeature(LanguageFeature.ConsistentExplicitEqualsForPrimitiveWrappers))
|
||||
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
|
||||
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
|
||||
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
|
||||
@@ -158,8 +160,6 @@ class GenerationState @JvmOverloads constructor(
|
||||
var hasResult: Boolean = false
|
||||
}
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
|
||||
val isCallAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS)
|
||||
val isReceiverAssertionsDisabled: Boolean =
|
||||
configuration.getBoolean(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS) ||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// LANGUAGE_VERSION: 1.1
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_RUNTIME
|
||||
// FILE: test.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(J.BOOL_NULL.equals(null), true)
|
||||
assertEquals(J.CHAR_NULL.equals(null), true)
|
||||
assertEquals(J.BYTE_NULL.equals(null), true)
|
||||
assertEquals(J.SHORT_NULL.equals(null), true)
|
||||
assertEquals(J.INT_NULL.equals(null), true)
|
||||
assertEquals(J.LONG_NULL.equals(null), true)
|
||||
assertEquals(J.FLOAT_NULL.equals(null), true)
|
||||
assertEquals(J.DOUBLE_NULL.equals(null), true)
|
||||
|
||||
assertEquals(J.BOOL_NULL == null, true)
|
||||
assertEquals(J.CHAR_NULL == null, true)
|
||||
assertEquals(J.BYTE_NULL == null, true)
|
||||
assertEquals(J.SHORT_NULL == null, true)
|
||||
assertEquals(J.INT_NULL == null, true)
|
||||
assertEquals(J.LONG_NULL == null, true)
|
||||
assertEquals(J.FLOAT_NULL == null, true)
|
||||
assertEquals(J.DOUBLE_NULL == null, true)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// FILE: J.java
|
||||
public class J {
|
||||
public static Boolean BOOL_NULL = null;
|
||||
public static Character CHAR_NULL = null;
|
||||
public static Byte BYTE_NULL = null;
|
||||
public static Short SHORT_NULL = null;
|
||||
public static Integer INT_NULL = null;
|
||||
public static Long LONG_NULL = null;
|
||||
public static Float FLOAT_NULL = null;
|
||||
public static Double DOUBLE_NULL = null;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_RUNTIME
|
||||
// FILE: test.kt
|
||||
import kotlin.test.*
|
||||
|
||||
fun box(): String {
|
||||
assertFailsWith<NullPointerException> { J.BOOL_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.CHAR_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.BYTE_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.SHORT_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.INT_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.LONG_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.FLOAT_NULL.equals(null) }
|
||||
assertFailsWith<NullPointerException> { J.DOUBLE_NULL.equals(null) }
|
||||
|
||||
assertEquals(J.BOOL_NULL == null, true)
|
||||
assertEquals(J.CHAR_NULL == null, true)
|
||||
assertEquals(J.BYTE_NULL == null, true)
|
||||
assertEquals(J.SHORT_NULL == null, true)
|
||||
assertEquals(J.INT_NULL == null, true)
|
||||
assertEquals(J.LONG_NULL == null, true)
|
||||
assertEquals(J.FLOAT_NULL == null, true)
|
||||
assertEquals(J.DOUBLE_NULL == null, true)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// FILE: J.java
|
||||
public class J {
|
||||
public static Boolean BOOL_NULL = null;
|
||||
public static Character CHAR_NULL = null;
|
||||
public static Byte BYTE_NULL = null;
|
||||
public static Short SHORT_NULL = null;
|
||||
public static Integer INT_NULL = null;
|
||||
public static Long LONG_NULL = null;
|
||||
public static Float FLOAT_NULL = null;
|
||||
public static Double DOUBLE_NULL = null;
|
||||
}
|
||||
+12
@@ -12388,6 +12388,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv11.kt")
|
||||
public void testEqualsNull_lv11() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv11.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv12.kt")
|
||||
public void testEqualsNull_lv12() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv12.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/hashCode.kt");
|
||||
|
||||
@@ -12388,6 +12388,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv11.kt")
|
||||
public void testEqualsNull_lv11() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv11.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv12.kt")
|
||||
public void testEqualsNull_lv12() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv12.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/hashCode.kt");
|
||||
|
||||
@@ -12388,6 +12388,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv11.kt")
|
||||
public void testEqualsNull_lv11() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv11.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNull_lv12.kt")
|
||||
public void testEqualsNull_lv12() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/equalsNull_lv12.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/platformTypes/primitives/hashCode.kt");
|
||||
|
||||
@@ -61,6 +61,7 @@ enum class LanguageFeature(
|
||||
LateinitLocalVariables(KOTLIN_1_2),
|
||||
InnerClassInEnumEntryClass(KOTLIN_1_2),
|
||||
CallableReferencesToClassMembersWithEmptyLHS(KOTLIN_1_2),
|
||||
ConsistentExplicitEqualsForPrimitiveWrappers(KOTLIN_1_2),
|
||||
JvmPackageName(KOTLIN_1_2),
|
||||
|
||||
RestrictionOfValReassignmentViaBackingField(KOTLIN_1_3),
|
||||
|
||||
Reference in New Issue
Block a user