xamarin-macios/runtime/xamarin-support.m

216 строки
6.7 KiB
Objective-C

#import <Foundation/Foundation.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
#include <iconv.h>
#include "xamarin/xamarin.h"
#include "runtime-internal.h"
#include "monotouch-support.h"
void
xamarin_os_log (os_log_t logger, os_log_type_t type, const char *message)
{
// Logging a dynamic string as-is using %{public}s is, strictly speaking,
// an antipattern. However, there is no way to call os_log directly from
// C#. It can only be called from Objective-C, and even then it requires
// that the format string be compiled into the binary. This really is
// our only option to pass a string from C# into os_log.
if (logger == NULL)
logger = OS_LOG_DEFAULT;
os_log_with_type (logger, type, "%{public}s", message);
}
const char *
xamarin_get_locale_country_code ()
{
// COOP: no managed memory access: any mode.
NSLocale *locale = [NSLocale currentLocale];
NSString *cc = [locale objectForKey: NSLocaleCountryCode];
if (cc == NULL) {
// Assume the US if the country isn't available.
return strdup ("US");
}
return strdup ([cc UTF8String]);
}
void
xamarin_log (const unsigned short *unicodeMessage)
{
// COOP: no managed memory access: any mode.
unsigned int length = 0;
const unsigned short *ptr = unicodeMessage;
while (*ptr++)
length += sizeof (unsigned short);
NSString *msg = [[NSString alloc] initWithBytes: unicodeMessage length: length encoding: NSUTF16LittleEndianStringEncoding];
#if TARGET_OS_WATCH && defined (__arm__) // maybe make this configurable somehow?
const char *utf8 = [msg UTF8String];
NSUInteger len = [msg lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; // does not include NULL
fwrite (utf8, 1, len, stdout);
if (len == 0 || utf8 [len - 1] != '\n')
fwrite ("\n", 1, 1, stdout);
fflush (stdout);
#else
if (length > 4096) {
// Write in chunks of max 4096 characters; older versions of iOS seems to have a bug where NSLog may hang with long strings (!).
// https://github.com/xamarin/maccore/issues/1014
const char *utf8 = [msg UTF8String];
NSUInteger len = [msg lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; // does not include NULL
const size_t max_size = 4096;
while (len > 0) {
size_t chunk_size = len > max_size ? max_size : len;
// Try to not break in the middle of a line, by looking backwards for a newline
while (chunk_size > 0 && utf8 [chunk_size] != 0 && utf8 [chunk_size] != '\n')
chunk_size--;
if (chunk_size == 0) {
// No newline found, break in the middle.
chunk_size = len > max_size ? max_size : len;
}
NSLog (@"%.*s", (int) chunk_size, utf8);
len -= chunk_size;
utf8 += chunk_size;
}
} else {
NSLog (@"%@", msg);
}
#endif
[msg release];
}
// NOTE: The timezone functions are duplicated in mono, so if you're going to modify here, it would be nice
// if we modify there.
//
// See in Mono sdks/ios/runtime/runtime.m
void*
xamarin_timezone_get_data (const char *name, uint32_t *size)
{
// COOP: no managed memory access: any mode.
NSTimeZone *tz = nil;
if (name) {
NSString *n = [[NSString alloc] initWithUTF8String: name];
tz = [[[NSTimeZone alloc] initWithName:n] autorelease];
[n release];
} else {
tz = [NSTimeZone localTimeZone];
}
NSData *data = [tz data];
*size = (uint32_t) [data length];
void* result = malloc (*size);
[data getBytes: result length: *size];
return result;
}
char**
xamarin_timezone_get_names (uint32_t *count)
{
// COOP: no managed memory access: any mode.
NSArray *array = [NSTimeZone knownTimeZoneNames];
*count = (uint32_t) array.count;
char** result = (char**) malloc (sizeof (char*) * (*count));
for (unsigned long i = 0; i < *count; i++) {
NSString *s = [array objectAtIndex: i];
result [i] = strdup (s.UTF8String);
}
return result;
}
//
// Returns the geopolitical region ID of the local timezone.
char *
xamarin_timezone_get_local_name ()
{
NSTimeZone *tz = nil;
tz = [NSTimeZone localTimeZone];
NSString *name = [tz name];
return (name != nil) ? strdup ([name UTF8String]) : strdup ("Local");
}
#if !TARGET_OS_WATCH && !TARGET_OS_TV && !(TARGET_OS_MACCATALYST && defined (DOTNET))
void
xamarin_start_wwan (const char *uri)
{
// COOP: no managed memory access: any mode.
#if defined(__i386__) || defined (__x86_64__)
return;
#else
unsigned char buf[1];
CFStringRef host = CFStringCreateWithCString (kCFAllocatorDefault, uri, kCFStringEncodingUTF8);
CFStringRef get = CFStringCreateWithCString (kCFAllocatorDefault, "GET", kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithString (kCFAllocatorDefault, host, nil);
CFHTTPMessageRef message = CFHTTPMessageCreateRequest (kCFAllocatorDefault, get, url, kCFHTTPVersion1_1);
CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest (kCFAllocatorDefault, message);
CFReadStreamScheduleWithRunLoop (stream, CFRunLoopGetCurrent (), kCFRunLoopCommonModes);
if (CFReadStreamOpen (stream)) {
// CFStreamStatus status = CFReadStreamGetStatus (stream);
// PRINT ("CFStreamStatus %i", status);
// note: some earlier iOS7 beta returned 1 (Opening) instead of 2 (Open) - a bit more time was needed or
// CFReadStreamRead blocks (and never return)
CFReadStreamRead (stream, buf, 1);
}
// that will remove it from the runloop (so we do it even if open failed)
CFReadStreamClose (stream);
CFRelease (stream);
CFRelease (host);
CFRelease (get);
CFRelease (url);
CFRelease (message);
#endif
}
#endif /* !TARGET_OS_WATCH && !TARGET_OS_TV && !TARGET_OS_MACCATALYST */
#if defined (MONOTOUCH)
// called from mono-extensions/mcs/class/corlib/System/Environment.iOS.cs
const char *
xamarin_GetFolderPath (int folder)
{
// COOP: no managed memory access: any mode.
// NSUInteger-based enum (and we do not want corlib exposed to 32/64 bits differences)
NSSearchPathDirectory dd = (NSSearchPathDirectory) folder;
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:dd inDomains:NSUserDomainMask] lastObject];
NSString *path = [url path];
return strdup ([path UTF8String]);
}
#endif /* defined (MONOTOUCH) */
#if defined (__arm64__)
// there are no objc_msgSend[Super]_stret functions on ARM64 but the managed
// code might have (e.g. linker is off) references to the symbol, which makes
// it impossible to disable dlsym and, for example, run dontlink on devices
// https://bugzilla.xamarin.com/show_bug.cgi?id=36569#c4
void objc_msgSend_stret (void)
{
PRINT ("Unimplemented objc_msgSend_stret");
abort ();
}
void objc_msgSendSuper_stret (void)
{
PRINT ("Unimplemented objc_msgSendSuper_stret");
abort ();
}
#endif
#ifdef MONOMAC
// <quote>Do not hard-code this parameter as a C string.</quote>
// works on iOS (where we don't need it) and crash on macOS
const char *
xamarin_encode_CGAffineTransform ()
{
// COOP: no managed memory access: any mode.
return @encode (CGAffineTransform);
}
#endif