|
| 1 | +#import <string.h> |
| 2 | +#import "NSSlowTaggedLocalizedString.h" |
| 3 | +#import <dlfcn.h> |
| 4 | +#import <objc/runtime.h> |
| 5 | +#import <Foundation/Foundation.h> |
| 6 | + |
| 7 | +/* |
| 8 | + This horrific mess is simulating the new-in-macOS-Ventura tagged pointer strings, |
| 9 | + which can have lengths >15 characters, which can cause problems in SmallString, |
| 10 | + which used to assume that would never happen. Once CI is running on Ventura or |
| 11 | + later, this can be rewritten to use a regular NSLocalizedString. |
| 12 | + */ |
| 13 | + |
| 14 | +@implementation NSSlowTaggedLocalizedString |
| 15 | + |
| 16 | ++ (instancetype) createTestString { |
| 17 | +#if __LP64__ |
| 18 | + static dispatch_once_t onceToken; |
| 19 | + dispatch_once(&onceToken, ^{ |
| 20 | + [[[NSString alloc] init] release]; //Make sure NSString is initialized |
| 21 | + Class tagClass = objc_lookUpClass("NSTaggedPointerString"); |
| 22 | + Class ourClass = [NSSlowTaggedLocalizedString class]; |
| 23 | + |
| 24 | + Method fastCString = class_getInstanceMethod(ourClass, @selector(_fastCStringContents:)); |
| 25 | + class_replaceMethod(tagClass, @selector(_fastCStringContents:), method_getImplementation(fastCString), method_getTypeEncoding(fastCString)); |
| 26 | + |
| 27 | + Method length = class_getInstanceMethod(ourClass, @selector(length)); |
| 28 | + class_replaceMethod(tagClass, @selector(length), method_getImplementation(length), method_getTypeEncoding(length)); |
| 29 | + |
| 30 | + Method charIndex = class_getInstanceMethod(ourClass, @selector(characterAtIndex:)); |
| 31 | + class_replaceMethod(tagClass, @selector(characterAtIndex:), method_getImplementation(charIndex), method_getTypeEncoding(charIndex)); |
| 32 | + |
| 33 | + Method fastChars = class_getInstanceMethod(ourClass, @selector(_fastCharacterContents)); |
| 34 | + class_replaceMethod(tagClass, @selector(_fastCharacterContents), method_getImplementation(fastChars), method_getTypeEncoding(fastChars)); |
| 35 | + |
| 36 | + Method retain = class_getInstanceMethod(ourClass, @selector(retain)); |
| 37 | + class_replaceMethod(tagClass, @selector(retain), method_getImplementation(retain), method_getTypeEncoding(retain)); |
| 38 | + |
| 39 | + Method release = class_getInstanceMethod(ourClass, @selector(release)); |
| 40 | + class_replaceMethod(tagClass, @selector(release), method_getImplementation(release), method_getTypeEncoding(release)); |
| 41 | + |
| 42 | + Method typeID = class_getInstanceMethod(ourClass, @selector(_cfTypeID)); |
| 43 | + class_replaceMethod(tagClass, @selector(_cfTypeID), method_getImplementation(typeID), method_getTypeEncoding(typeID)); |
| 44 | + |
| 45 | + Method description = class_getInstanceMethod(ourClass, @selector(description)); |
| 46 | + class_replaceMethod(tagClass, @selector(description), method_getImplementation(description), method_getTypeEncoding(description)); |
| 47 | + |
| 48 | + Method getBytes = class_getInstanceMethod(ourClass, @selector(getBytes:maxLength:usedLength:encoding:options:range:remainingRange:)); |
| 49 | + class_replaceMethod(tagClass, @selector(getBytes:maxLength:usedLength:encoding:options:range:remainingRange:), method_getImplementation(getBytes), method_getTypeEncoding(getBytes)); |
| 50 | + }); |
| 51 | + return (NSSlowTaggedLocalizedString *)(void *)CFStringCreateWithCString(NULL, "a", kCFStringEncodingASCII); //make a tagged pointer string |
| 52 | +#else |
| 53 | + return nil; |
| 54 | +#endif |
| 55 | +} |
| 56 | + |
| 57 | +static const char *contents = NULL; |
| 58 | + |
| 59 | ++ (void) setContents: (const char *)newContents { |
| 60 | + const char *oldContents = contents; |
| 61 | + if (newContents) { |
| 62 | + contents = strdup(newContents); |
| 63 | + } else { |
| 64 | + contents = NULL; |
| 65 | + } |
| 66 | + free((void *)oldContents); |
| 67 | +} |
| 68 | + |
| 69 | +- (const char *)_fastCStringContents:(BOOL)nullTerminationRequired { |
| 70 | + return contents; |
| 71 | +} |
| 72 | + |
| 73 | +- (uint64_t)length { |
| 74 | + return strlen(contents); |
| 75 | +} |
| 76 | + |
| 77 | +- (id)copyWithZone:(id)unused { |
| 78 | + return self; |
| 79 | +} |
| 80 | + |
| 81 | +- (uint16_t)characterAtIndex:(NSUInteger)index { |
| 82 | + if (index >= [self length]) { |
| 83 | + abort(); |
| 84 | + } |
| 85 | + return (uint16_t)contents[index]; |
| 86 | +} |
| 87 | + |
| 88 | +- (void *) _fastCharacterContents { |
| 89 | + return nil; |
| 90 | +} |
| 91 | + |
| 92 | +- (id) retain { return self; } |
| 93 | +- (oneway void) release {} |
| 94 | + |
| 95 | +- (uint64_t)_cfTypeID { |
| 96 | + return 7; //CFString |
| 97 | +} |
| 98 | + |
| 99 | +- (id) description { |
| 100 | + return self; |
| 101 | +} |
| 102 | + |
| 103 | +- (BOOL)getBytes:(void *)buffer maxLength:(uint64_t)max usedLength:(uint64_t *)used encoding:(uint64_t)encoding options:(uint64_t)options range:(NSRange)range remainingRange:(NSRange *)leftover { |
| 104 | + assert(encoding == 1 /* ASCII */ || encoding == 4 /* UTF8 */); |
| 105 | + strncpy(buffer, contents, max); |
| 106 | + if (strlen(contents) > max) { |
| 107 | + leftover->location = max; |
| 108 | + leftover->length = strlen(contents) - max; |
| 109 | + return false; |
| 110 | + } |
| 111 | + leftover->location = 0; |
| 112 | + leftover->length = 0; |
| 113 | + return true; |
| 114 | +} |
| 115 | + |
| 116 | +@end |
0 commit comments