Preserve original NSString in kotlin.String if possible

Thus fix localized strings in Kotlin.
This commit is contained in:
Svyatoslav Scherbina
2019-11-08 12:41:03 +03:00
committed by SvyatoslavScherbina
parent 57ada1c2cb
commit ac74fb0a5a
8 changed files with 145 additions and 13 deletions
+5
View File
@@ -3565,6 +3565,11 @@ interopTest("interop_objc_smoke") {
if (project.target instanceof KonanTarget.IOS_X64) {
UtilsKt.codesign(project, "$buildDir/libobjcsmoke.dylib")
}
copy {
from("interop/objc/Localizable.stringsdict")
into("$testOutputLocal/$name/$target/en.lproj/")
}
}
}
@@ -1078,16 +1078,41 @@ func testGH3525() throws {
}
func testStringConversion() throws {
let test = TestStringConversion()
func test1() throws {
let test = TestStringConversion()
let buffer = NSMutableString()
buffer.append("a")
test.str = buffer
buffer.append("b")
let buffer = NSMutableString()
buffer.append("a")
test.str = buffer
buffer.append("b")
try assertEquals(actual: buffer, expected: "ab")
// Ensure test.str isn't affected by buffer mutation:
try assertEquals(actual: test.str as! NSString, expected: "a")
try assertEquals(actual: buffer, expected: "ab")
// Ensure test.str isn't affected by buffer mutation:
try assertEquals(actual: test.str as! NSString, expected: "a")
}
func ensureNoCopy(nsStr: NSString) throws {
let test = TestStringConversion()
test.str = nsStr
let nsStr2 = test.str as! NSString
// Ensure no additional NSString created on both conversions:
try assertTrue(nsStr === nsStr2)
}
func test2() throws {
var str = "a"
str += NSObject().description
try ensureNoCopy(nsStr: str as NSString)
try ensureNoCopy(nsStr: NSString("abc"))
try ensureNoCopy(nsStr: NSString(format: "%d%d%d", 3, 2, 1))
}
try test1()
try test2()
}
// -------- Execution of the test --------
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- AUTO-GENERATED FILE. DO NOT MODIFY. -->
<!-- This file was automatically generated by the LocoLaser tool. -->
<!-- It should not be modified by hand. -->
<plist version="1.0">
<dict>
<key>screen_main_plural_string</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@value@</string>
<key>value</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>other</key>
<string>Plural: %d apples</string>
<key>one</key>
<string>Plural: one apple</string>
<key>zero</key>
<string>Plural: no apples</string>
</dict>
</dict>
</dict>
</plist>
@@ -1,4 +1,4 @@
language = Objective-C
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h
headerFilter = **/smoke.h Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
objc/objc.h
objc/objc.h Foundation/NSBundle.h
+15
View File
@@ -224,3 +224,18 @@ Protocol* getPrinterProtocol() {
@interface TestAllocNoRetain : NSObject
@property BOOL ok;
@end;
@interface CustomString : NSString
- (instancetype)initWithValue:(int)value;
@property int value;
@end;
CustomString* _Nonnull createCustomString(int value) {
return [[CustomString alloc] initWithValue:value];
}
int getCustomStringValue(CustomString* str) {
return str.value;
}
extern BOOL customStringDeallocated;
@@ -29,6 +29,8 @@ fun run() {
testAllocNoRetain()
testNSOutputStreamToMemoryConstructor()
testExportObjCClass()
testCustomString()
testLocalizedStrings()
assertEquals(2, ForwardDeclaredEnum.TWO.value)
@@ -457,6 +459,28 @@ fun testExportObjCClass() {
xor (TestExportObjCClass4().objCClassName == TestExportObjCClass34Name))
}
fun testCustomString() {
assertFalse(customStringDeallocated)
fun test() = autoreleasepool {
val str: String = createCustomString(321)
assertEquals("321", str)
assertEquals("CustomString", str.objCClassName)
assertEquals(321, getCustomStringValue(str))
}
test()
kotlin.native.internal.GC.collect()
assertTrue(customStringDeallocated)
}
fun testLocalizedStrings() {
val key = "screen_main_plural_string"
val localizedString = NSBundle.mainBundle.localizedStringForKey(key, value = "", table = "Localizable")
val string = NSString.localizedStringWithFormat(localizedString, 5)
assertEquals("Plural: 5 apples", string)
}
private val Any.objCClassName: String
get() = object_getClassName(this)!!.toKString()
+31
View File
@@ -289,3 +289,34 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
return self;
}
@end;
BOOL customStringDeallocated = NO;
@implementation CustomString {
NSString* delegate;
}
- (instancetype)initWithValue:(int)value {
if (self = [super init]) {
self->delegate = @(value).description;
self.value = value;
}
return self;
}
- (unichar)characterAtIndex:(NSUInteger)index {
return [self->delegate characterAtIndex:index];
}
- (NSUInteger)length {
return self->delegate.length;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (void)dealloc {
customStringDeallocated = YES;
}
@end;
+8 -3
View File
@@ -19,6 +19,7 @@
#if KONAN_OBJC_INTEROP
#import <objc/runtime.h>
#import <CoreFoundation/CFString.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import "Memory.h"
@@ -69,12 +70,16 @@ OBJ_GETTER(Kotlin_Interop_CreateKStringFromNSString, NSString* str) {
RETURN_OBJ(nullptr);
}
size_t length = [str length];
NSRange range = {0, length};
CFStringRef immutableCopyOrSameStr = CFStringCreateCopy(nullptr, (CFStringRef)str);
auto length = CFStringGetLength(immutableCopyOrSameStr);
CFRange range = {0, length};
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, length, OBJ_RESULT)->array();
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
[str getCharacters:rawResult range:range];
CFStringGetCharacters(immutableCopyOrSameStr, range, rawResult);
result->obj()->meta_object()->associatedObject_ = (void*)immutableCopyOrSameStr;
RETURN_OBJ(result->obj());
}