Fix KT-26478: Objective-C-objects-class-name-is-null-in-ClassCastExceptions-message
#KT-26478 Fixed Merge-request: KT-MR-6405 Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
class KT26478
|
||||
|
||||
@Test
|
||||
fun testKT26478compiletime() {
|
||||
val exception = assertFailsWith<ClassCastException> {
|
||||
NSBundle() as KT26478
|
||||
}
|
||||
assertEquals("class NSBundle cannot be cast to class KT26478", exception.message)
|
||||
}
|
||||
|
||||
val bundle26478:Any = NSBundle()
|
||||
@Test
|
||||
fun testKT26478runtime() {
|
||||
val exception = assertFailsWith<ClassCastException> {
|
||||
bundle26478 as KT26478
|
||||
}
|
||||
assertEquals("class NSBundle cannot be cast to class KT26478", exception.message)
|
||||
}
|
||||
@@ -7,3 +7,12 @@ fun testKT35544() {
|
||||
}
|
||||
assertEquals("class kotlin.Int cannot be cast to class objcTests.NSString", exception.message)
|
||||
}
|
||||
|
||||
val bundle35544: Any = NSBundle()
|
||||
@Test
|
||||
fun testKT35544runtime() {
|
||||
val exception = assertFailsWith<TypeCastException> {
|
||||
bundle35544 as NSString
|
||||
}
|
||||
assertEquals("class NSBundle cannot be cast to class objcTests.NSString", exception.message)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,24 @@ void DisposeCString(char* cstring) {
|
||||
if (cstring) std_support::free(cstring);
|
||||
}
|
||||
|
||||
ObjHeader* CreatePermanentStringFromCString(const char* nullTerminatedUTF8) {
|
||||
const char* end = nullTerminatedUTF8 + strlen(nullTerminatedUTF8);
|
||||
size_t count = utf8::with_replacement::utf16_length(nullTerminatedUTF8, end);
|
||||
size_t headerSize = alignUp(sizeof(ArrayHeader), alignof(char16_t));
|
||||
size_t arraySize = headerSize + count * sizeof(char16_t);
|
||||
|
||||
ArrayHeader* header = (ArrayHeader*)std_support::calloc(arraySize, 1);
|
||||
header->obj()->typeInfoOrMeta_ = setPointerBits((TypeInfo *)theStringTypeInfo, OBJECT_TAG_PERMANENT_CONTAINER);
|
||||
header->count_ = count;
|
||||
utf8::with_replacement::utf8to16(nullTerminatedUTF8, end, CharArrayAddressOfElementAt(header, 0));
|
||||
|
||||
return header->obj();
|
||||
}
|
||||
|
||||
void FreePermanentStringForTests(ArrayHeader* header) {
|
||||
std_support::free(header);
|
||||
}
|
||||
|
||||
// String.kt
|
||||
OBJ_GETTER(Kotlin_String_replace, KString thiz, KChar oldChar, KChar newChar) {
|
||||
auto count = thiz->count_;
|
||||
|
||||
@@ -30,6 +30,8 @@ OBJ_GETTER(CreateStringFromCString, const char* cstring);
|
||||
OBJ_GETTER(CreateStringFromUtf8, const char* utf8, uint32_t lengthBytes);
|
||||
char* CreateCStringFromString(KConstRef kstring);
|
||||
void DisposeCString(char* cstring);
|
||||
ObjHeader* CreatePermanentStringFromCString(const char* nullTerminatedUTF8);
|
||||
void FreePermanentStringForTests(ArrayHeader* header); // to make ASAN happy, in hostRuntimeTests call FreePermanentStringForTests() after CreatePermanentStringFromCString()
|
||||
|
||||
OBJ_GETTER(StringFromUtf8Buffer, const char* start, size_t size);
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "KString.h"
|
||||
#include "Natives.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "std_support/CStdlib.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
void checkContentsEquality(ArrayHeader* actual, const char16_t* expected) {
|
||||
EXPECT_THAT(actual->count_, std::char_traits<char16_t>::length(expected));
|
||||
for (size_t i=0; i<actual->count_; i++) {
|
||||
EXPECT_THAT(*CharArrayAddressOfElementAt(actual, i), expected[i]);
|
||||
}
|
||||
EXPECT_THAT(actual->obj()->permanent(), true);
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_ascii) {
|
||||
const char* ascii = "Ascii";
|
||||
EXPECT_THAT(strlen(ascii), 5);
|
||||
const char16_t* expected = u"Ascii";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 5);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(ascii)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_misc) {
|
||||
const char* non_ascii = "-£öü²ソニーΣℜ∫♣‰€";
|
||||
EXPECT_THAT(strlen(non_ascii), 35);
|
||||
const char16_t* expected = u"-£öü²ソニーΣℜ∫♣‰€";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 14);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(non_ascii)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_surrogates) {
|
||||
const char* surrogates = "😃𓄀🌀🐀𝜀";
|
||||
EXPECT_THAT(strlen(surrogates), 20);
|
||||
const char16_t* expected = u"😃𓄀🌀🐀𝜀";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 10);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(surrogates)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_empty) {
|
||||
const char* empty = "";
|
||||
EXPECT_THAT(strlen(empty), 0);
|
||||
const char16_t* expected = u"";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 0);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(empty)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_impossible) {
|
||||
const char* impossible = "\xff";
|
||||
EXPECT_THAT(strlen(impossible), 1);
|
||||
const char16_t* expected = u"\xfffd";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 1);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(impossible)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
|
||||
TEST(KStringTest, CreatePermanentStringFromCString_overlong) {
|
||||
const char* overlong = "\xc0\xaf";
|
||||
EXPECT_THAT(strlen(overlong), 2);
|
||||
const char16_t* expected = u"\xfffd\xfffd";
|
||||
EXPECT_THAT(std::char_traits<char16_t>::length(expected), 2);
|
||||
|
||||
auto actual = CreatePermanentStringFromCString(overlong)->array();
|
||||
checkContentsEquality(actual, expected);
|
||||
FreePermanentStringForTests(actual); // to prevent Address Sanitizer test failures, permanently allocated strings must be deallocated before test end
|
||||
}
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#import "Types.h"
|
||||
#import "Memory.h"
|
||||
#include "Natives.h"
|
||||
#include "ObjCInterop.h"
|
||||
#include "KString.h"
|
||||
|
||||
#if KONAN_OBJC_INTEROP
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#import "Runtime.h"
|
||||
#import "Mutex.hpp"
|
||||
#import "Exceptions.h"
|
||||
#import "Natives.h"
|
||||
#include "std_support/CStdlib.hpp"
|
||||
#include "std_support/Map.hpp"
|
||||
#include "std_support/String.hpp"
|
||||
@@ -727,6 +728,7 @@ static void buildITable(TypeInfo* result, const std_support::map<ClassId, std_su
|
||||
}
|
||||
|
||||
static const TypeInfo* createTypeInfo(
|
||||
const char* className,
|
||||
const TypeInfo* superType,
|
||||
const std_support::vector<const TypeInfo*>& superInterfaces,
|
||||
const std_support::vector<VTableElement>& vtable,
|
||||
@@ -785,7 +787,7 @@ static const TypeInfo* createTypeInfo(
|
||||
}
|
||||
|
||||
result->packageName_ = nullptr;
|
||||
result->relativeName_ = nullptr; // TODO: add some info.
|
||||
result->relativeName_ = CreatePermanentStringFromCString(className);
|
||||
result->writableInfo_ = (WritableTypeInfo*)std_support::calloc(1, sizeof(WritableTypeInfo));
|
||||
|
||||
for (size_t i = 0; i < vtable.size(); ++i) result->vtable()[i] = vtable[i];
|
||||
@@ -994,7 +996,7 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
|
||||
|
||||
// TODO: consider forbidding the class being abstract.
|
||||
|
||||
const TypeInfo* result = createTypeInfo(superType, addedInterfaces, vtable, interfaceVTables,
|
||||
const TypeInfo* result = createTypeInfo(class_getName(clazz), superType, addedInterfaces, vtable, interfaceVTables,
|
||||
superITable, superITableSize, itableEqualsSuper, fieldsInfo);
|
||||
|
||||
// TODO: it will probably never be requested, since such a class can't be instantiated in Kotlin.
|
||||
|
||||
@@ -38,8 +38,8 @@ internal class KClassImpl<T : Any>(private val typeInfo: NativePtr) : KClass<T>
|
||||
internal val fullName: String?
|
||||
get() {
|
||||
val relativeName = getRelativeName(typeInfo, false) ?: return null
|
||||
val packageName = getPackageName(typeInfo, false)!!
|
||||
return if (packageName.isEmpty()) relativeName else "$packageName.$relativeName"
|
||||
val packageName: String? = getPackageName(typeInfo, false)
|
||||
return if (packageName?.isEmpty() ?: true) relativeName else "$packageName.$relativeName"
|
||||
}
|
||||
|
||||
internal fun findAssociatedObjectImpl(key: KClassImpl<*>): Any? =
|
||||
|
||||
Reference in New Issue
Block a user