diff --git a/Xamarin.iOS.sln b/Xamarin.iOS.sln
index 2fe3708e9b4d..2d650fb19122 100644
--- a/Xamarin.iOS.sln
+++ b/Xamarin.iOS.sln
@@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2BFA13F8
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "introspection-ios", "tests\introspection\iOS\introspection-ios.csproj", "{208744BD-504E-47D7-9A98-1CF02454A6DA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libmonotouch", "runtime\libmonotouch.csproj", "{8A5B637C-E4FF-4145-B887-9347020100F4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xamios", "src\xamios.csproj", "{E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -168,6 +172,74 @@ Global
{208744BD-504E-47D7-9A98-1CF02454A6DA}.Release|iPhone.Build.0 = Release|iPhone
{208744BD-504E-47D7-9A98-1CF02454A6DA}.DebugStaticRegistrar|iPhone.ActiveCfg = DebugStaticRegistrar|iPhone
{208744BD-504E-47D7-9A98-1CF02454A6DA}.DebugStaticRegistrar|iPhone.Build.0 = DebugStaticRegistrar|iPhone
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_2_0_Release|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_3_5_Release|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.net_4_0_Release|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.silverlight_Release|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.winphone_Release|Any CPU.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.Release|iPhone.Build.0 = Release|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.DebugStaticRegistrar|iPhone.ActiveCfg = Debug|Any CPU
+ {8A5B637C-E4FF-4145-B887-9347020100F4}.DebugStaticRegistrar|iPhone.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_2_0_Release|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_3_5_Release|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.net_4_0_Release|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.silverlight_Release|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.winphone_Release|Any CPU.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.Release|iPhone.Build.0 = Release|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.DebugStaticRegistrar|iPhone.ActiveCfg = Debug|Any CPU
+ {E1F334C3-8F77-46C9-A28B-A8E9BAEA9FE5}.DebugStaticRegistrar|iPhone.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{208744BD-504E-47D7-9A98-1CF02454A6DA} = {2BFA13F8-B568-48BF-9A70-9D43F718C523}
diff --git a/runtime/EXCEPTIONS.md b/runtime/EXCEPTIONS.md
new file mode 100644
index 000000000000..b9b0aa591077
--- /dev/null
+++ b/runtime/EXCEPTIONS.md
@@ -0,0 +1,127 @@
+History
+====================
+
+Historically we've attempted to convert Objective-C exceptions to managed exceptions
+by adding a handler to be notified of uncaught Objective-C exceptions [1], but that one
+major problem: it did not handle *caught* Objective-C exceptions, where the exception
+handler would just abort.
+
+This is common in iOS apps, where the main runloop adds an Objective-C @try/@catch
+handler that just aborts, preventing us from being notified of the Objectice-C exception.
+
+This approach also has another problem: the Objective-C exception handling mechanism
+could unwind through managed code, which would result in all kinds of inexplicable
+behavior (if there was an Objective-C exception handler that _didn't_ abort the app):
+
+ try {
+ doSomethingThatCausesAnObjectiveCException ();
+ } finally {
+ Console.WriteLine ("Done!");
+ }
+
+If there happened to be an Objective-C exception handler in a stack frame further
+up, the Console.WriteLine would never be executed.
+
+Another frequent problem would occur in an IDE debugger if it tried to evaluate
+every property for an object. If there were properties not supported on the
+executing platform, it would throw an Objective-C exception (selector not found)
+that would terminate the app.
+
+The reverse problem also existed: throwing a managed exception that the native-to-managed
+boundary didn't catch, would make the mono runtime unwind through native frames,
+causing a similar set of inexplicable behavior:
+
+ [obj selectorThatManagedCodeHasOverriddenAndThrowAManagedException];
+ NSLog (@"done!");
+
+The NSLog statement would never be executed.
+
+[1] By using [NSSetUncaughtExceptionHandler](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSSetUncaughtExceptionHandler).
+
+Exception marshaling
+====================
+
+There are two parts to exception marshaling: Objective-C exceptions
+and managed exceptions.
+
+In both cases we offer a managed event the app can subscribe to to
+be notified, and then the app can choose what to do:
+
+1. Convert the managed/Objective-C exception to its counterpart
+ (i.e. managed exceptions are converted to an Objective-C exception,
+ and Objective-C exceptions are converted to a managed exception).
+
+2. Abort.
+
+3. Rethrow the original exception (this is not available when
+ using the Coop GC, which is the only option on watchOS).
+
+Managed exception marshaling
+----------------------------
+
+This is the simplest case, we just make sure to catch any managed
+exceptions that reaches native code. There are a few sources:
+
+1. When we call mono_runtime_invoke in our trampolines.
+
+ In this case mono_runtime_invoke will catch any exceptions
+ and return them to us (without unwinding anything else).
+
+2. When managed code gives a delegate to native code, and
+ native code calls that delegate.
+
+ This is still a TODO, we need to use this new API to be
+ notified when the mono runtime detects this case:
+ https://github.com/mono/mono/pull/2948
+
+Objective-C exception marshaling
+--------------------------------
+
+This is more complicated, because there must be an Objective-C
+exception handler on the managed-to-native boundary frame,
+catching the Objective-C exception.
+
+We have two approaches for this:
+
+1. Custom wrappers of the objc_msgSend functions with an
+ Objective-C exception handler. These have to be written
+ in assembly code, since it's not possible to do it in C.
+ At runtime we inject a dllmap into the process, which
+ redirects any P/invoke to the objc_msgSend functions to
+ these custom wrappers.
+
+ Since we can't write assembly code for bitcode targets
+ (watchOS), another approach is still required though.
+
+ This is the used for iOS/tvOS/watchOS simulator builds
+ (both 32-bit and 64-bit) and Mac/64 bits. It does not
+ work on Mac/32 bits because that platform does not use
+ 0-cost exceptions like the other platforms (it uses
+ setjmp/longjmp instead, and would require a very
+ different implementation in assembly code).
+
+ The assembly code for these wrappers is in the
+ trampolines-[arch]-objc_msgSend* files.
+
+ The documentation about 0-cost exceptions is scarce,
+ but here are a few documents which were helpful:
+
+ https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Exceptions/Articles/Exceptions64Bit.html
+ https://sourceware.org/binutils/docs/as/CFI-directives.html
+ http://mentorembedded.github.io/cxx-abi/abi-eh.html
+ http://llvm.org/docs/ExceptionHandling.html
+ http://www.opensource.apple.com/source/llvmCore/llvmCore-3425.0.33/docs/ExceptionHandling.rst
+ http://stackoverflow.com/a/7535848
+ http://www.darlinghq.org/for-developers/exception-handling-on-os-x
+
+ The wrappers themselves were first created by writing
+ something similar in C, and then telling clang to
+ dump the corresponding assembly code (by passing
+ `--save-temps -fverbose-asm` to clang).
+
+2. At build time generate custom wrappers of every P/Invoke
+ to the objc_msgSend functions, and modify the P/Invoke to
+ call these generated wrappers.
+
+ This is used for iOS/tvOS/watchOS device builds, and
+ Mac/32 bits.
diff --git a/runtime/Makefile b/runtime/Makefile
index c2af5da9c071..ddd34c8f563c 100644
--- a/runtime/Makefile
+++ b/runtime/Makefile
@@ -20,8 +20,8 @@ SHIPPED_HEADERS += \
xamarin/runtime.h \
SHARED_SOURCES += mono-runtime.m bindings.m bindings-generated.m shared.m runtime.m trampolines.m trampolines-invoke.m xamarin-support.m nsstring-localization.m trampolines-varargs.m
-SHARED_I386_SOURCES += trampolines-i386.m trampolines-i386-asm.s
-SHARED_X86_64_SOURCES += trampolines-x86_64.m trampolines-x86_64-asm.s
+SHARED_I386_SOURCES += trampolines-i386.m trampolines-i386-asm.s trampolines-i386-objc_msgSend.s trampolines-i386-objc_msgSendSuper.s trampolines-i386-objc_msgSend_stret.s trampolines-i386-objc_msgSendSuper_stret.s
+SHARED_X86_64_SOURCES += trampolines-x86_64.m trampolines-x86_64-asm.s trampolines-x86_64-objc_msgSend.s trampolines-x86_64-objc_msgSendSuper.s trampolines-x86_64-objc_msgSend_stret.s trampolines-x86_64-objc_msgSendSuper_stret.s
SHARED_HEADERS += shared.h product.h delegates.h runtime-internal.h $(SHARED_INC) $(SHIPPED_HEADERS) trampolines-internal.h slinked-list.h
SHARED_FILES = $(SHARED_SOURCES) $(SHARED_HEADERS) $(SHARED_I386_SOURCES) $(SHARED_X86_64_SOURCES)
diff --git a/runtime/delegates.t4 b/runtime/delegates.t4
index 658b85ed567a..4c90714aea4e 100644
--- a/runtime/delegates.t4
+++ b/runtime/delegates.t4
@@ -19,6 +19,14 @@
"NSException *", "IntPtr", "exc"
) { WrappedManagedFunction = "ThrowNSException" },
+ new XDelegate ("int", "int", "xamarin_create_ns_exception",
+ "NSException *", "IntPtr", "exc"
+ ) { WrappedManagedFunction = "CreateNSException" },
+
+ new XDelegate ("NSException *", "IntPtr", "xamarin_unwrap_ns_exception",
+ "int", "int", "exc_handle"
+ ) { WrappedManagedFunction = "UnwrapNSException" },
+
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_block_wrapper_creator",
"MonoObject *", "IntPtr", "method",
"int", "int", "parameter"
@@ -147,6 +155,14 @@
new XDelegate ("char *", "IntPtr", "xamarin_lookup_managed_type_name",
"Class", "IntPtr", "klass"
) { WrappedManagedFunction = "LookupManagedTypeName" },
+
+ new XDelegate ("MarshalManagedExceptionMode", "MarshalManagedExceptionMode", "xamarin_on_marshal_managed_exception",
+ "int", "int", "exception"
+ ) { WrappedManagedFunction = "OnMarshalManagedException" },
+
+ new XDelegate ("MarshalObjectiveCExceptionMode", "MarshalObjectiveCExceptionMode", "xamarin_on_marshal_objectivec_exception",
+ "id", "IntPtr", "exception"
+ ) { WrappedManagedFunction = "OnMarshalObjectiveCException" },
};
delegates.CalculateLengths ();
#><#+
diff --git a/runtime/exports.t4 b/runtime/exports.t4
index eacc81e0d90e..d6d2314fd493 100644
--- a/runtime/exports.t4
+++ b/runtime/exports.t4
@@ -218,6 +218,14 @@
#endregion
+ #region metadata/object-internals.h
+
+ new Export (true, "void", "mono_set_pending_exception",
+ "MonoException *", "exc"
+ ),
+
+ #endregion
+
#region metadata/assembly.h
new Export ("void", "mono_set_assemblies_path",
@@ -288,6 +296,14 @@
"MonoMethod *", "method"
),
+ new Export ("void", "mono_dllmap_insert",
+ "MonoImage *", "assembly",
+ "const char *", "dll",
+ "const char *", "func",
+ "const char *", "tdll",
+ "const char *", "tfunc"
+ ),
+
#endregion
#region metadata/appdomain.h
diff --git a/runtime/libmonotouch.csproj b/runtime/libmonotouch.csproj
index 425c726b59a4..35536d76b975 100644
--- a/runtime/libmonotouch.csproj
+++ b/runtime/libmonotouch.csproj
@@ -83,5 +83,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/runtime/runtime-internal.h b/runtime/runtime-internal.h
index 52a995b7bc40..1bb3472b580c 100644
--- a/runtime/runtime-internal.h
+++ b/runtime/runtime-internal.h
@@ -26,4 +26,19 @@
void *xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method);
+/*
+ * XamarinGCHandle
+ *
+ * This is an ObjC type that ties the lifetime of a GCHandle to the lifetime of itself.
+ * It stores a GCHandle, and frees the GCHandle in dealloc.
+ */
+@interface XamarinGCHandle : NSObject {
+@public
+ int handle;
+}
++(XamarinGCHandle *) createWithHandle: (int) handle;
+-(void) dealloc;
+-(int) getHandle;
+@end
+
#endif /* __RUNTIME_INTERNAL_H__ */
diff --git a/runtime/runtime.m b/runtime/runtime.m
index 81a37f214095..5d0559e32b33 100644
--- a/runtime/runtime.m
+++ b/runtime/runtime.m
@@ -74,6 +74,13 @@
const char *xamarin_arch_name = NULL;
#endif
+#if TARGET_OS_WATCH
+bool xamarin_is_gc_coop = true;
+#else
+bool xamarin_is_gc_coop = false;
+#endif
+enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDefault;
+enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDefault;
/* Callbacks */
@@ -141,6 +148,8 @@
struct Delegates Delegates;
struct Trampolines Trampolines;
RegistrationData* RegistrationData;
+ enum MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode;
+ enum MarshalManagedExceptionMode MarshalManagedExceptionMode;
};
static struct Trampolines trampolines = {
@@ -858,25 +867,32 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
xamarin_free (type_name);
}
-void
-xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data)
+static NSMutableString *
+print_all_exceptions (MonoObject *exc)
{
- int counter = 0;
- // fetch the field, since the property might have been linked away.
- MonoClassField *inner_exception = mono_class_get_field_from_name (mono_object_get_class (exc), "inner_exception");
NSMutableString *str = [[NSMutableString alloc] init];
+ // fetch the field, since the property might have been linked away.
+ int counter = 0;
+ MonoClassField *inner_exception = mono_class_get_field_from_name (mono_object_get_class (exc), "_innerException");
do {
print_exception (exc, counter > 0, str);
if (inner_exception) {
mono_field_get_value (exc, inner_exception, &exc);
} else {
- LOG ("Could not find the field inner_exception in System.Exception\n");
+ LOG ("Could not find the field _innerException in System.Exception\n");
break;
}
} while (counter++ < 10 && exc);
- NSLog (@"%@", str);
+ [str autorelease];
+ return str;
+}
+
+void
+xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data)
+{
+ NSLog (@"%@", print_all_exceptions (exc));
abort ();
}
@@ -967,6 +983,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
xamarin_initialize_dynamic_runtime (NULL);
#endif
+ xamarin_insert_dllmap ();
+
mono_trace_set_log_handler (log_callback, NULL);
mono_trace_set_print_handler (print_callback);
mono_trace_set_printerr_handler (print_callback);
@@ -1010,6 +1028,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
options.Trampolines = trampolines;
options.RegistrationData = ®istration_data;
+ options.MarshalObjectiveCExceptionMode = xamarin_marshal_objectivec_exception_mode;
+ options.MarshalManagedExceptionMode = xamarin_marshal_managed_exception_mode;
params [0] = &options;
@@ -1134,15 +1154,18 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
case _C_PTR:
return objc_skip_type (++type);
case _C_BFLD:
+ type++;
+ while (*type && *type >= '0' && *type <= '9')
+ type++;
+ return type;
case _C_ATOM:
case _C_VECTOR:
case _C_CONST:
- assert (0);
case _C_ARY_E:
case _C_UNION_E:
case _C_STRUCT_E:
- assert (0);
-
+ xamarin_assertion_message ("Unhandled type encoding: %s", type);
+ break;
case _C_ARY_B: {
do {
type++;
@@ -1173,10 +1196,10 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
return ++type;
}
-
+ default:
+ xamarin_assertion_message ("Unsupported type encoding: %s", type);
+ break;
}
-
- assert (0);
}
int
@@ -1198,13 +1221,27 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
case _C_ULNG_LNG: return sizeof (unsigned long long);
case _C_FLT: return sizeof (float);
case _C_DBL: return sizeof (double);
- case _C_BFLD: assert (0);
case _C_BOOL: return sizeof (BOOL);
case _C_VOID: return 0;
- case _C_UNDEF: assert (0);
case _C_PTR: return sizeof (void *);
case _C_CHARPTR: return sizeof (char *);
- case _C_ATOM: assert (0);
+ case _C_BFLD: {
+ // Example: [NSDecimalNumberPlaceholder initWithDecimal:] = @28@0:4{?=b8b4b1b1b18[8S]}8
+ int bits = 0;
+ int bc = 1;
+ while (type [bc] >= '0' && type [bc] <= '9') {
+ bits = bits * 10 + (type [bc] - '0');
+ bc++;
+ }
+ if (bits % sizeof (void *) == 0)
+ return bits / sizeof (void *);
+ return 1 + (bits / sizeof (void *));
+ }
+ case _C_UNDEF:
+ case _C_ATOM:
+ case _C_VECTOR:
+ xamarin_assertion_message ("Unhandled type encoding: %s", type);
+ break;
case _C_ARY_B: {
int size = 0;
int len = atoi (type+1);
@@ -1252,10 +1289,8 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
return size;
}
- case _C_VECTOR: assert (0);
- case _C_CONST: assert (0);
// The following are from table 6-2 here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
- // case 'r': _C_CONST
+ case 'r': // _C_CONST
case 'n':
case 'N':
case 'o':
@@ -1687,6 +1722,107 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
}
}
+void
+xamarin_process_nsexception (NSException *ns_exception)
+{
+ MarshalObjectiveCExceptionMode mode;
+ XamarinGCHandle *exc_handle;
+
+ mode = xamarin_on_marshal_objectivec_exception (ns_exception);
+
+ if (mode == MarshalObjectiveCExceptionModeDefault)
+ mode = xamarin_is_gc_coop ? MarshalObjectiveCExceptionModeThrowManagedException : MarshalObjectiveCExceptionModeUnwindManagedCode;
+
+ switch (mode) {
+ case MarshalObjectiveCExceptionModeUnwindManagedCode:
+ if (xamarin_is_gc_coop)
+ xamarin_assertion_message ("Cannot unwind managed frames for Objective-C exceptions when the GC is in cooperative mode.");
+ @throw ns_exception;
+ break;
+ case MarshalObjectiveCExceptionModeThrowManagedException:
+ exc_handle = [[ns_exception userInfo] objectForKey: @"XamarinManagedExceptionHandle"];
+ if (exc_handle != NULL) {
+ int handle = [exc_handle getHandle];
+ MonoObject *exc = mono_gchandle_get_target (handle);
+ mono_set_pending_exception ((MonoException *) exc);
+ } else {
+ int handle = xamarin_create_ns_exception (ns_exception);
+ MonoObject *exc = mono_gchandle_get_target (handle);
+ mono_set_pending_exception ((MonoException *) exc);
+ mono_gchandle_free (handle);
+ }
+ break;
+ case MarshalObjectiveCExceptionModeAbort:
+ default:
+ xamarin_assertion_message ("Aborting due to unhandled Objective-C exception:\n%s\n", [[ns_exception description] UTF8String]);
+ break;
+ }
+}
+
+void
+xamarin_process_managed_exception (MonoObject *exception)
+{
+ if (exception == NULL)
+ return;
+
+ MarshalManagedExceptionMode mode;
+
+ int handle = mono_gchandle_new (exception, false);
+ mode = xamarin_on_marshal_managed_exception (handle);
+ mono_gchandle_free (handle);
+
+ if (mode == MarshalManagedExceptionModeDefault)
+ mode = xamarin_is_gc_coop ? MarshalManagedExceptionModeThrowObjectiveCException : MarshalManagedExceptionModeUnwindNativeCode;
+
+ switch (mode) {
+ case MarshalManagedExceptionModeUnwindNativeCode:
+ if (xamarin_is_gc_coop)
+ xamarin_assertion_message ("Cannot unwind native frames for managed exceptions when the GC is in cooperative mode.");
+ mono_raise_exception ((MonoException *) exception);
+ break;
+ case MarshalManagedExceptionModeThrowObjectiveCException: {
+ int handle = mono_gchandle_new (exception, false);
+ NSException *ns_exc = xamarin_unwrap_ns_exception (handle);
+
+ if (ns_exc != NULL) {
+ mono_gchandle_free (handle);
+ @throw ns_exc;
+ } else {
+ NSString *name = [NSString stringWithUTF8String: xamarin_type_get_full_name (mono_class_get_type (mono_object_get_class (exception)))];
+ char *message = fetch_exception_property_string (exception, "get_Message", true);
+ NSString *reason = [NSString stringWithUTF8String: message];
+ mono_free (message);
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject: [XamarinGCHandle createWithHandle: handle] forKey: @"XamarinManagedExceptionHandle"];
+ @throw [[NSException alloc] initWithName: name reason: reason userInfo: userInfo];
+ }
+ break;
+ }
+ case MarshalManagedExceptionModeAbort:
+ default:
+ xamarin_assertion_message ("Aborting due to:\n%s\n", [print_all_exceptions (exception) UTF8String]);
+ break;
+ }
+}
+
+void
+xamarin_insert_dllmap ()
+{
+#if defined (OBJC_ZEROCOST_EXCEPTIONS) && (defined (__i386__) || defined (__x86_64__))
+ if (xamarin_marshal_objectivec_exception_mode == MarshalObjectiveCExceptionModeDisable)
+ return;
+#if DYLIB
+ const char *lib = "libxammac.dylib";
+#else
+ const char *lib = "__Internal";
+#endif
+ mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend", lib, "xamarin_dyn_objc_msgSend");
+ mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper", lib, "xamarin_dyn_objc_msgSendSuper");
+ mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend_stret", lib, "xamarin_dyn_objc_msgSend_stret");
+ mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper_stret", lib, "xamarin_dyn_objc_msgSendSuper_stret");
+ LOG (PRODUCT ": Added dllmap for objc_msgSend");
+#endif // defined (__i386__) || defined (__x86_64__)
+}
+
/*
* Object unregistration:
*
@@ -1842,3 +1978,27 @@ -(int) xamarinGetGCHandle
{
return xamarin_debug_mode;
}
+
+/*
+ * XamarinGCHandle
+ */
+@implementation XamarinGCHandle
++(XamarinGCHandle *) createWithHandle: (int) h
+{
+ XamarinGCHandle *rv = [[XamarinGCHandle alloc] init];
+ rv->handle = h;
+ [rv autorelease];
+ return rv;
+}
+
+-(void) dealloc
+{
+ mono_gchandle_free (handle);
+ [super dealloc];
+}
+
+-(int) getHandle
+{
+ return handle;
+}
+@end
diff --git a/runtime/shared.h b/runtime/shared.h
index 62585b468760..2220c991a04e 100644
--- a/runtime/shared.h
+++ b/runtime/shared.h
@@ -22,6 +22,7 @@ void initialize_cocoa_threads (init_cocoa_func *func);
void install_nsautoreleasepool_hooks ();
id xamarin_init_nsthread (id obj, bool is_direct, id target, SEL sel, id arg);
+void xamarin_insert_dllmap ();
/*
* Block support
diff --git a/runtime/trampolines-i386-asm.s b/runtime/trampolines-i386-asm.s
index a097d05556bd..c847e0a63d22 100644
--- a/runtime/trampolines-i386-asm.s
+++ b/runtime/trampolines-i386-asm.s
@@ -18,9 +18,16 @@
#if __i386__
+.subsections_via_symbols
+.text
+
_xamarin_i386_common_trampoline:
+.cfi_startproc
pushl %ebp
+.cfi_def_cfa_offset 8
+.cfi_offset %ebp, -8
movl %esp, %ebp
+.cfi_def_cfa_register %ebp
pushl %esi # we use %esi as a pointer to our CallState struct. It's a preserved register, so we need to save it.
@@ -85,6 +92,8 @@ L_no_floating_point:
L_double_stret_return:
retl $0x4
+.cfi_endproc
+
#
# trampolines
#
diff --git a/runtime/trampolines-i386-objc_msgSend-copyframe.inc b/runtime/trampolines-i386-objc_msgSend-copyframe.inc
new file mode 100644
index 000000000000..4bb4823c875a
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSend-copyframe.inc
@@ -0,0 +1,53 @@
+
+ #
+ # input stack layout:
+ # %esp+20: ...
+ # %esp+16: second arg
+ # %esp+12: first arg
+ # %esp+8: sel
+ # %esp+4: this
+ # %esp: buffer
+ # and %ebp+8 = %esp
+ #
+ # We extend the stack (big enough for all the arguments again),
+ # and copy all the arguments as-is there, before
+ # calling objc_msgSend with those copied arguments.
+ # The only difference is that this method has an exception handler.
+ #
+
+ call _xamarin_get_frame_length # get_frame_length (this, sel)
+
+ # use eax to extend the stack, but it needs to be aligned to 16 bytes first
+ addl $15,%eax
+ shrl $4,%eax
+ sall $4,%eax
+ subl %eax,%esp
+ # store the number somewhere so we can restore the stack pointer later
+ movl %eax,-16(%ebp)
+
+ # copy arguments from old location in the stack to new location in the stack
+ # %ecx will hold the amount of bytes left to copy
+ # %esi the current src location
+ # %edi the current dst location
+
+ # %ecx will already be a multiple of 4, since the abi requires it
+ # (arguments smaller than 4 bytes are extended to 4 bytes according to
+ # http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4)
+
+ movl -16(%ebp),%ecx # ecx = frame_length
+ leal 8(%ebp),%esi # esi = address of first argument we got (buffer)
+ movl %esp,%edi # edi = address of the bottom of the stack
+
+L_start:
+ cmpl $0,%ecx #
+ je L_end # while (left != 0) {
+ subl $4,%ecx # len -= 4
+ movl (%esi,%ecx),%eax # tmp = src [len]
+ movl %eax,(%edi,%ecx) # dst [len] = tmp
+ jmp L_start # }
+L_end:
+
+ movaps -40(%ebp), %xmm0
+ movaps -56(%ebp), %xmm1
+ movaps -72(%ebp), %xmm2
+ movaps -88(%ebp), %xmm3
diff --git a/runtime/trampolines-i386-objc_msgSend-post.inc b/runtime/trampolines-i386-objc_msgSend-post.inc
new file mode 100644
index 000000000000..d40088bec3b7
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSend-post.inc
@@ -0,0 +1,93 @@
+Lafterinvoke:
+ addl -16(%ebp), %esp
+ addl $108, %esp
+ popl %ebx
+ popl %edi
+ popl %esi
+ popl %ebp
+ retl
+Lcatchhandler:
+ cmpl $1, %edx
+ movl %eax, (%esp)
+ jne Lnomatchexception
+
+ # check if xamarin_marshal_objectivec_exception_mode == disable, if so, just don't handle the exception
+ call Lloadpcrelative1
+Lloadpcrelative1:
+ popl %ecx
+ cmpl $4, L_xamarin_marshal_objectivec_exception_mode$non_lazy_ptr-Lloadpcrelative1(%ecx)
+ je Lnomatchexception
+
+ calll _objc_begin_catch
+Lcatchbegin:
+ movl %eax, (%esp)
+ calll _xamarin_process_nsexception
+Lcatchend:
+ calll _objc_end_catch
+ jmp Lafterinvoke
+Lcatchcatchhandler:
+ calll _objc_end_catch
+Lnomatchexception:
+ calll __Unwind_Resume
+Lfunc_end0:
+.cfi_endproc
+
+# exception table
+ .section __TEXT,__gcc_except_tab
+ .align 2
+GCC_except_table0:
+Lexception0:
+ .byte 255 ## @LPStart Encoding = omit
+ .byte 155 ## @TType Encoding = indirect pcrel sdata4
+ .asciz "\274" ## @TType base offset
+ .byte 3 ## Call site Encoding = udata4
+ .byte 52 ## Call site table length
+Lset0 = Lbeforeinvoke-Lfunc_begin0 ## >> Call Site 1 <<
+ .long Lset0
+Lset1 = Lafterinvoke-Lbeforeinvoke ## Call between Lbeforeinvoke and Lafterinvoke
+ .long Lset1
+Lset2 = Lcatchhandler-Lfunc_begin0 ## jumps to Lcatchhandler
+ .long Lset2
+ .byte 1 ## On action: 1
+Lset3 = Lafterinvoke-Lfunc_begin0 ## >> Call Site 2 <<
+ .long Lset3
+Lset4 = Lcatchbegin-Lafterinvoke ## Call between Lafterinvoke and Lcatchbegin
+ .long Lset4
+ .long 0 ## has no landing pad
+ .byte 0 ## On action: cleanup
+Lset5 = Lcatchbegin-Lfunc_begin0 ## >> Call Site 3 <<
+ .long Lset5
+Lset6 = Lcatchend-Lcatchbegin ## Call between Lcatchbegin and Lcatchend
+ .long Lset6
+Lset7 = Lcatchcatchhandler-Lfunc_begin0 ## jumps to Lcatchcatchhandler
+ .long Lset7
+ .byte 0 ## On action: cleanup
+Lset8 = Lcatchend-Lfunc_begin0 ## >> Call Site 4 <<
+ .long Lset8
+Lset9 = Lfunc_end0-Lcatchend ## Call between Lcatchend and Lfunc_end0
+ .long Lset9
+ .long 0 ## has no landing pad
+ .byte 0 ## On action: cleanup
+ .byte 1 ## >> Action Record 1 <<
+ ## Catch TypeInfo 1
+ .byte 0 ## No further actions
+ ## >> Catch TypeInfos <<
+Ltmp9: ## TypeInfo 1
+ .long L_OBJC_EHTYPE_$_NSException$non_lazy_ptr-Ltmp9
+ .align 2
+
+ .section __DATA,__objc_imageinfo,regular,no_dead_strip
+L_OBJC_IMAGE_INFO:
+ .long 0
+ .long 0
+
+ .section __IMPORT,__pointers,non_lazy_symbol_pointers
+L_OBJC_EHTYPE_$_NSException$non_lazy_ptr:
+ .indirect_symbol _OBJC_EHTYPE_$_NSException
+ .long 0
+L___objc_personality_v0$non_lazy_ptr:
+ .indirect_symbol ___objc_personality_v0
+ .long 0
+L_xamarin_marshal_objectivec_exception_mode$non_lazy_ptr:
+ .indirect_symbol _xamarin_marshal_objectivec_exception_mode
+ .long 0
diff --git a/runtime/trampolines-i386-objc_msgSend-pre.inc b/runtime/trampolines-i386-objc_msgSend-pre.inc
new file mode 100644
index 000000000000..0fe9994eee6f
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSend-pre.inc
@@ -0,0 +1,27 @@
+
+# function start
+# preserve some registers
+# create stack space to call get_frame_length
+
+Lfunc_begin0:
+.cfi_startproc
+.cfi_personality 155, L___objc_personality_v0$non_lazy_ptr
+.cfi_lsda 16, Lexception0
+
+ pushl %ebp
+.cfi_def_cfa_offset 8
+.cfi_offset %ebp, -8
+ movl %esp, %ebp
+.cfi_def_cfa_register %ebp
+
+ pushl %esi # %ebp-4
+ pushl %edi # %ebp-8
+ pushl %ebx # %ebp-12
+ # we need 64 bytes to store xmm0-3, and those 64 bytes need to be 16-byte aligned.
+ # we need 4 bytes to store the result for get_frame_length, which we store in %ebp-16
+ # then 4 more bytes for stack space for the call to get_frame_length (which takes 2 arguments)
+ subl $108, %esp # to store xmm0-3
+ movaps %xmm0, -40(%ebp)
+ movaps %xmm1, -56(%ebp)
+ movaps %xmm2, -72(%ebp)
+ movaps %xmm3, -88(%ebp)
diff --git a/runtime/trampolines-i386-objc_msgSend.s b/runtime/trampolines-i386-objc_msgSend.s
new file mode 100644
index 000000000000..4b63e559ffbe
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSend.s
@@ -0,0 +1,25 @@
+#if __i386__ && MONOTOUCH
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSend
+_xamarin_dyn_objc_msgSend:
+
+#include "trampolines-i386-objc_msgSend-pre.inc"
+
+ # int frame_length = get_frame_length (this, sel)
+ movl 12(%ebp), %eax
+ movl %eax, 4(%esp) # push sel
+ movl 8(%ebp), %eax
+ movl %eax, (%esp) # push this
+
+#include "trampolines-i386-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+ calll _objc_msgSend
+
+#include "trampolines-i386-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-i386-objc_msgSendSuper.s b/runtime/trampolines-i386-objc_msgSendSuper.s
new file mode 100644
index 000000000000..785154340947
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSendSuper.s
@@ -0,0 +1,26 @@
+#if __i386__ && MONOTOUCH
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSendSuper
+_xamarin_dyn_objc_msgSendSuper:
+
+#include "trampolines-i386-objc_msgSend-pre.inc"
+
+ # int frame_length = get_frame_length (this, sel)
+ movl 12(%ebp), %eax
+ movl %eax, 4(%esp) # push sel
+ movl 8(%ebp), %eax
+ movl (%eax), %eax
+ movl %eax, (%esp) # push this
+
+#include "trampolines-i386-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+ calll _objc_msgSendSuper
+
+#include "trampolines-i386-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-i386-objc_msgSendSuper_stret.s b/runtime/trampolines-i386-objc_msgSendSuper_stret.s
new file mode 100644
index 000000000000..1f5e42cb9678
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSendSuper_stret.s
@@ -0,0 +1,27 @@
+#if __i386__ && MONOTOUCH
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSendSuper_stret
+_xamarin_dyn_objc_msgSendSuper_stret:
+
+#include "trampolines-i386-objc_msgSend-pre.inc"
+
+ # int frame_length = get_frame_length (this, sel)
+ movl 16(%ebp), %eax
+ movl %eax, 4(%esp) # push sel
+ movl 12(%ebp), %eax
+ movl (%eax), %eax
+ movl %eax, (%esp) # push this
+
+#include "trampolines-i386-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+ calll _objc_msgSendSuper_stret
+ subl $4, %esp
+
+#include "trampolines-i386-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-i386-objc_msgSend_stret.s b/runtime/trampolines-i386-objc_msgSend_stret.s
new file mode 100644
index 000000000000..6f78b19f5694
--- /dev/null
+++ b/runtime/trampolines-i386-objc_msgSend_stret.s
@@ -0,0 +1,26 @@
+#if __i386__ && MONOTOUCH
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSend_stret
+_xamarin_dyn_objc_msgSend_stret:
+
+#include "trampolines-i386-objc_msgSend-pre.inc"
+
+ # int frame_length = get_frame_length (this, sel)
+ movl 16(%ebp), %eax
+ movl %eax, 4(%esp) # push sel
+ movl 12(%ebp), %eax
+ movl %eax, (%esp) # push this
+
+#include "trampolines-i386-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+ calll _objc_msgSend_stret
+ subl $4, %esp
+
+#include "trampolines-i386-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-invoke.m b/runtime/trampolines-invoke.m
index a7865783eb59..ac97803a5408 100644
--- a/runtime/trampolines-invoke.m
+++ b/runtime/trampolines-invoke.m
@@ -21,6 +21,8 @@
void
xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_func iterator, marshal_return_value_func marshal_return_value, void *context)
{
+ MonoObject *exception = NULL;
+ MonoObject **exception_ptr = xamarin_marshal_managed_exception_mode == MarshalManagedExceptionModeDisable ? NULL : &exception;
bool is_static = (type & Tramp_Static) == Tramp_Static;
bool is_ctor = type == Tramp_Ctor;
@@ -353,7 +355,7 @@
xamarin_set_nsobject_handle (retval, self);
xamarin_set_nsobject_flags (retval, NSObjectFlagsNativeRef);
- mono_runtime_invoke (method, retval, (void **) arg_ptrs, NULL);
+ mono_runtime_invoke (method, retval, (void **) arg_ptrs, exception_ptr);
xamarin_create_managed_ref (self, retval, true);
xamarin_register_nsobject (retval, self);
@@ -366,7 +368,7 @@
fprintf (stderr, "\n");
#endif
- retval = mono_runtime_invoke (method, mthis, (void **) arg_ptrs, NULL);
+ retval = mono_runtime_invoke (method, mthis, (void **) arg_ptrs, exception_ptr);
#ifdef TRACE
fprintf (stderr, " called managed method with %i arguments: ", num_arg);
@@ -451,4 +453,6 @@
} else if (*ret_type != 'v') {
marshal_return_value (context, ret_type, [sig methodReturnLength], retval, mono_signature_get_return_type (msig), (desc.semantic & ArgumentSemanticRetainReturnValue) != 0, method);
}
-}
\ No newline at end of file
+
+ xamarin_process_managed_exception (exception);
+}
diff --git a/runtime/trampolines-x86_64-asm.s b/runtime/trampolines-x86_64-asm.s
index d055700f1e3b..8cd31a15543c 100644
--- a/runtime/trampolines-x86_64-asm.s
+++ b/runtime/trampolines-x86_64-asm.s
@@ -16,9 +16,16 @@
#if __x86_64__
+.subsections_via_symbols
+.text
+
_xamarin_x86_64_common_trampoline:
+.cfi_startproc
pushq %rbp
+.cfi_def_cfa_offset 16
+.cfi_offset %rbp, -16
movq %rsp, %rbp
+.cfi_def_cfa_register %rbp
subq $0xC0, %rsp # allocate 192 bytes from the stack
# todo: verify alignment.
movq %r11, (%rsp)
@@ -52,6 +59,7 @@ _xamarin_x86_64_common_trampoline:
popq %rbp
ret
+.cfi_endproc
#
# trampolines
diff --git a/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc b/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc
new file mode 100644
index 000000000000..10d36eeff642
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSend-copyframe.inc
@@ -0,0 +1,46 @@
+
+ call _xamarin_get_frame_length
+
+ # r12 holds the amount of stack space we need
+ movq %rax, %r12
+ # first align stack requirement to 16 bytes
+ addq $15,%r12
+ shrq $4,%r12
+ salq $4,%r12
+ # then make space for the arguments
+ subq %r12, %rsp
+
+ # copy arguments from old location in the stack to new location in the stack
+ # %rcx will hold the amount of bytes left to copy
+ # %rsi the current src location
+ # %rdi the current dst location
+
+ movq %r12, %rcx # rcx = frame_length
+ leaq 16(%rbp),%rsi # rsi = address of first argument we got
+ movq %rsp,%rdi # rdi = address of the bottom of the stack
+
+L_start:
+ cmpq $0,%rcx #
+ je L_end # while (left != 0) {
+ subq $8,%rcx # len -= 8
+ movq (%rsi,%rcx),%rax # tmp = src [len]
+ movq %rax,(%rdi,%rcx) # dst [len] = tmp
+ jmp L_start # }
+L_end:
+
+ movq -16(%rbp), %rdi
+ movq -24(%rbp), %rsi
+ movq -32(%rbp), %rdx
+ movq -40(%rbp), %rcx
+ movq -48(%rbp), %r8
+ movq -56(%rbp), %r9
+ movq -64(%rbp), %rax
+ /* skip 8 bytes so that this is 16-byte aligned */
+ movaps -80(%rbp), %xmm0
+ movaps -96(%rbp), %xmm1
+ movaps -112(%rbp), %xmm2
+ movaps -128(%rbp), %xmm3
+ movaps -144(%rbp), %xmm4
+ movaps -160(%rbp), %xmm5
+ movaps -176(%rbp), %xmm6
+ movaps -192(%rbp), %xmm7
diff --git a/runtime/trampolines-x86_64-objc_msgSend-post.inc b/runtime/trampolines-x86_64-objc_msgSend-post.inc
new file mode 100644
index 000000000000..ebc7d2c1071b
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSend-post.inc
@@ -0,0 +1,73 @@
+
+Lafterinvoke:
+ addq $208, %rsp
+ addq %r12, %rsp
+ movq -8(%rbp), %r12
+ popq %rbp
+ retq
+Lcatchhandler:
+ cmpl $1, %edx
+ movq %rax, %rdi
+ jne Lnomatchexception
+
+ # check if xamarin_marshal_objectivec_exception_mode == disable, if so, just don't handle the exception
+ leal _xamarin_marshal_objectivec_exception_mode(%rip), %edx
+ cmpl $4, %edx
+ je Lnomatchexception
+
+ callq _objc_begin_catch
+Lcatchbegin:
+ movq %rax, %rdi
+ callq _xamarin_process_nsexception
+Lcatchend:
+ callq _objc_end_catch
+ jmp Lafterinvoke
+Lcatchcatchhandler:
+ callq _objc_end_catch
+Lnomatchexception:
+ callq __Unwind_Resume
+Lfunc_end2:
+ .cfi_endproc
+
+# exception table
+ .section __TEXT,__gcc_except_tab
+ .align 2
+GCC_except_table2:
+Lexception2:
+ .byte 255 ## @LPStart Encoding = omit
+ .byte 155 ## @TType Encoding = indirect pcrel sdata4
+ .asciz "\274" ## @TType base offset
+ .byte 3 ## Call site Encoding = udata4
+ .byte 52 ## Call site table length
+Lset0 = Lbeforeinvoke-Lfunc_begin2 ## >> Call Site 1 <<
+ .long Lset0
+Lset1 = Lafterinvoke-Lbeforeinvoke ## Call between Lbeforeinvoke and Lafterinvoke
+ .long Lset1
+Lset2 = Lcatchhandler-Lfunc_begin2 ## jumps to Lcatchhandler
+ .long Lset2
+ .byte 1 ## On action: 1
+Lset3 = Lafterinvoke-Lfunc_begin2 ## >> Call Site 2 <<
+ .long Lset3
+Lset4 = Lcatchbegin-Lafterinvoke ## Call between Lafterinvoke and Lcatchbegin
+ .long Lset4
+ .long 0 ## has no landing pad
+ .byte 0 ## On action: cleanup
+Lset5 = Lcatchbegin-Lfunc_begin2 ## >> Call Site 3 <<
+ .long Lset5
+Lset6 = Lcatchend-Lcatchbegin ## Call between Lcatchbegin and Lcatchend
+ .long Lset6
+Lset7 = Lcatchcatchhandler-Lfunc_begin2 ## jumps to Lcatchcatchhandler
+ .long Lset7
+ .byte 0 ## On action: cleanup
+Lset8 = Lcatchend-Lfunc_begin2 ## >> Call Site 4 <<
+ .long Lset8
+Lset9 = Lfunc_end2-Lcatchend ## Call between Lcatchend and Lfunc_end2
+ .long Lset9
+ .long 0 ## has no landing pad
+ .byte 0 ## On action: cleanup
+ .byte 1 ## >> Action Record 1 <<
+ ## Catch TypeInfo 1
+ .byte 0 ## No further actions
+ ## >> Catch TypeInfos <<
+ .long _OBJC_EHTYPE_$_NSException@GOTPCREL+4 ## TypeInfo 1
+ .align 2
diff --git a/runtime/trampolines-x86_64-objc_msgSend-pre.inc b/runtime/trampolines-x86_64-objc_msgSend-pre.inc
new file mode 100644
index 000000000000..ee324a25fedb
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSend-pre.inc
@@ -0,0 +1,34 @@
+
+# function start
+# preserve some registers
+
+Lfunc_begin2:
+.cfi_startproc
+.cfi_personality 155, ___objc_personality_v0
+.cfi_lsda 16, Lexception2
+
+ pushq %rbp
+.cfi_def_cfa_offset 16
+.cfi_offset %rbp, -16
+ movq %rsp, %rbp
+.cfi_def_cfa_register %rbp
+
+ # calculate the amount of stack space we need
+ subq $208,%rsp
+ movq %r12, -8(%rbp)
+ movq %rdi, -16(%rbp)
+ movq %rsi, -24(%rbp)
+ movq %rdx, -32(%rbp)
+ movq %rcx, -40(%rbp)
+ movq %r8, -48(%rbp)
+ movq %r9, -56(%rbp)
+ movq %rax, -64(%rbp)
+ /* skip 8 bytes so that this is 16-byte aligned */
+ movaps %xmm0, -80(%rbp)
+ movaps %xmm1, -96(%rbp)
+ movaps %xmm2, -112(%rbp)
+ movaps %xmm3, -128(%rbp)
+ movaps %xmm4, -144(%rbp)
+ movaps %xmm5, -160(%rbp)
+ movaps %xmm6, -176(%rbp)
+ movaps %xmm7, -192(%rbp)
diff --git a/runtime/trampolines-x86_64-objc_msgSend.s b/runtime/trampolines-x86_64-objc_msgSend.s
new file mode 100644
index 000000000000..3f7d3a71252e
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSend.s
@@ -0,0 +1,19 @@
+#if __x86_64__
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSend
+_xamarin_dyn_objc_msgSend:
+
+#include "trampolines-x86_64-objc_msgSend-pre.inc"
+
+#include "trampolines-x86_64-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+call _objc_msgSend
+
+#include "trampolines-x86_64-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-x86_64-objc_msgSendSuper.s b/runtime/trampolines-x86_64-objc_msgSendSuper.s
new file mode 100644
index 000000000000..7eeddaf2a3d9
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSendSuper.s
@@ -0,0 +1,22 @@
+#if __x86_64__
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSendSuper
+_xamarin_dyn_objc_msgSendSuper:
+
+#include "trampolines-x86_64-objc_msgSend-pre.inc"
+
+ # resolve objc_super* [handle, class_handle] to the actual instance
+ movq (%rdi), %rdi
+
+#include "trampolines-x86_64-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+call _objc_msgSendSuper
+
+#include "trampolines-x86_64-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s b/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s
new file mode 100644
index 000000000000..67122004aa4c
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSendSuper_stret.s
@@ -0,0 +1,23 @@
+#if __x86_64__
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSendSuper_stret
+_xamarin_dyn_objc_msgSendSuper_stret:
+
+#include "trampolines-x86_64-objc_msgSend-pre.inc"
+
+ # put (id, SEL) where xamarin_get_frame_length expects it
+ movq (%rsi), %rdi
+ movq %rdx, %rsi
+
+#include "trampolines-x86_64-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+call _objc_msgSendSuper_stret
+
+#include "trampolines-x86_64-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines-x86_64-objc_msgSend_stret.s b/runtime/trampolines-x86_64-objc_msgSend_stret.s
new file mode 100644
index 000000000000..411add4773ad
--- /dev/null
+++ b/runtime/trampolines-x86_64-objc_msgSend_stret.s
@@ -0,0 +1,23 @@
+#if __x86_64__
+
+.subsections_via_symbols
+.text
+
+.align 4
+.globl _xamarin_dyn_objc_msgSend_stret
+_xamarin_dyn_objc_msgSend_stret:
+
+#include "trampolines-x86_64-objc_msgSend-pre.inc"
+
+ # put (id, SEL) where xamarin_get_frame_length expects it
+ movq %rsi, %rdi
+ movq %rdx, %rsi
+
+#include "trampolines-x86_64-objc_msgSend-copyframe.inc"
+
+Lbeforeinvoke:
+call _objc_msgSend_stret
+
+#include "trampolines-x86_64-objc_msgSend-post.inc"
+
+#endif
diff --git a/runtime/trampolines.m b/runtime/trampolines.m
index 7380e744ac6f..d656f50d108c 100644
--- a/runtime/trampolines.m
+++ b/runtime/trampolines.m
@@ -14,6 +14,7 @@
#include
+#include "product.h"
#include "delegates.h"
#include "xamarin/xamarin.h"
#include "slinked-list.h"
@@ -123,15 +124,213 @@
}
}
-#if defined (__i386__)
+static const char *
+get_method_description (Class cls, SEL sel)
+{
+ Protocol **protocols;
+ unsigned int p_count;
+ Class p_cls = cls;
+ struct objc_method_description desc;
+
+ while (p_cls) {
+ protocols = class_copyProtocolList (p_cls, &p_count);
+ for (unsigned int i = 0; i < p_count; i++) {
+ desc = protocol_getMethodDescription (protocols [i], sel, YES, !class_isMetaClass (p_cls));
+ if (desc.types != NULL) {
+ free (protocols);
+ return desc.types;
+ }
+ }
+ free (protocols);
+ p_cls = class_getSuperclass (p_cls);
+ }
+
+ Method method = class_getInstanceMethod (cls, sel);
+ if (!method)
+ return NULL;
+ struct objc_method_description* m_desc;
+ m_desc = method_getDescription (method);
+ return m_desc ? m_desc->types : NULL;
+}
+
+static int
+count_until (const char *desc, char start, char end)
+{
+ // Counts the number of characters until a certain character is found: 'end'
+ // If the 'start' character is found, nesting is assumed, and an additional
+ // 'end' character must be found before the function returns.
+ int i = 1;
+ int sub = 0;
+ while (*desc) {
+ if (start == *desc) {
+ sub++;
+ } else if (end == *desc) {
+ sub--;
+ if (sub == 0)
+ return i;
+ }
+ i++;
+ // This is not multi-byte safe...
+ desc++;
+ }
+
+ fprintf (stderr, PRODUCT ": Unexpected type encoding, did not find end character '%c' in '%s'.", end, desc);
+
+ return i;
+}
+
+static int
+get_type_description_length (const char *desc)
+{
+ int length = 0;
+ // This function returns the length of the first encoded type string in desc.
+ switch (desc [0]) {
+ case _C_ID:
+ if (desc [1] == '?') {
+ // Example: [AVAssetImageGenerator generateCGImagesAsynchronouslyForTimes:completionHandler:] = 'v16@0:4@8@?12'
+ length = 2;
+ } else {
+ length = 1;
+ }
+ break;
+ case _C_CLASS:
+ case _C_SEL:
+ case _C_CHR:
+ case _C_UCHR:
+ case _C_SHT:
+ case _C_USHT:
+ case _C_INT:
+ case _C_UINT:
+ case _C_LNG:
+ case _C_ULNG:
+ case _C_LNG_LNG:
+ case _C_ULNG_LNG:
+ case _C_FLT:
+ case _C_DBL:
+ case _C_BOOL:
+ case _C_VOID:
+ case _C_CHARPTR:
+ length = 1;
+ break;
+ case _C_PTR:
+ length = 1;
+
+ // handle broken encoding where simd types don't show up at all
+ // Example: [GKPath pathWithPoints:count:radius:cyclical:] = '@24@0:4^8L12f16c20'
+ // Here we assume that we're pointing to a simd type if we find
+ // a number (i.e. only get the size of what we're pointing to
+ // if the next character isn't a number).
+ if (desc [1] < '0' || desc [1] > '9')
+ length += get_type_description_length (desc + 1);
+
+ break;
+ case _C_ARY_B:
+ length = count_until (desc, _C_ARY_B, _C_ARY_E);
+ break;
+ case _C_UNION_B:
+ length = count_until (desc, _C_UNION_B, _C_UNION_E);
+ break;
+ case _C_STRUCT_B:
+ length = count_until (desc, _C_STRUCT_B, _C_STRUCT_E);
+ break;
+ // The following are from table 6-2 here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
+ case 'r': // _C_CONST
+ case 'n':
+ case 'N':
+ case 'o':
+ case 'O':
+ case 'R':
+ case 'V':
+ length = 1 + get_type_description_length (desc + 1);
+ break;
+ case _C_BFLD:
+ length = 1;
+ break;
+ case _C_UNDEF:
+ case _C_ATOM:
+ case _C_VECTOR:
+ xamarin_assertion_message ("Unhandled type encoding: %s", desc);
+ break;
+ default:
+ xamarin_assertion_message ("Unsupported type encoding: %s", desc);
+ break;
+ }
+
+ // Every type encoding _may_ be followed by the stack frame offset for that type
+ while (desc [length] >= '0' && desc [length] <= '9')
+ length++;
+
+ return length;
+}
+
int
xamarin_get_frame_length (id self, SEL sel)
{
- NSMethodSignature *sig = [self methodSignatureForSelector: sel];
+ if (self == NULL)
+ return sizeof (void *) * 3; // we might be in objc_msgStret, in which case we'll need to copy three arguments.
+
+ // [NSDecimalNumber initWithDecimal:] has this descriptor: "@36@0:8{?=b8b4b1b1b18[8S]}16"
+ // which NSMethodSignature chokes on: NSInvalidArgumentException Reason: +[NSMethodSignature signatureWithObjCTypes:]: unsupported type encoding spec '{?}'
+ // So instead parse the description ourselves.
+
+ int length = 0;
+ Class cls = object_getClass (self);
+ const char *method_description = get_method_description (cls, sel);
+ const char *desc = method_description;
+ if (desc == NULL) {
+ // This happens with [[UITableViewCell appearance] backgroundColor]
+ @try {
+ NSMethodSignature *sig = [self methodSignatureForSelector: sel];
+ length = [sig frameLength];
+ } @catch (NSException *ex) {
+ length = sizeof (void *) * 64; // some high-ish number.
+ fprintf (stderr, PRODUCT ": Failed to calculate the frame size for the method [%s %s] (%s). Using a value of %i instead.\n", class_getName (cls), sel_getName (sel), [[ex description] UTF8String], length);
+ }
+ } else {
+ // The format of the method type encoding is described here: http://stackoverflow.com/a/11492151/183422
+ // the return type might have a number after it, which is the size of the argument frame
+ // first get this number (if it's there), and use it as a minimum value for the frame length
+ int rvlength = get_type_description_length (desc);
+ int min_length = 0;
+ if (rvlength > 0) {
+ const char *min_start = desc + rvlength;
+ // the number is at the end of the return type encoding, so find any numbers
+ // at the end of the type encoding.
+ while (min_start > desc && min_start [-1] >= '0' && min_start [-1] <= '9')
+ min_start--;
+ if (min_start < desc + rvlength) {
+ for (int i = 0; i < desc + rvlength - min_start; i++)
+ min_length = min_length * 10 + (min_start [i] - '0');
+ }
+ }
- return [sig frameLength];
+ // fprintf (stderr, "Found desc '%s' for [%s %s] with min frame length %i\n", desc, class_getName (cls), sel_getName (sel), min_length);
+
+ // skip the return value.
+ desc += rvlength;
+ while (*desc) {
+ int tl = xamarin_objc_type_size (desc);
+ // round up to pointer size
+ if (tl % sizeof (void *) != 0)
+ tl += sizeof (void *) - (tl % sizeof (void *));
+ length += tl;
+ // fprintf (stderr, " argument=%s length=%i totallength=%i\n", desc, tl, length);
+ desc += get_type_description_length (desc);
+ }
+
+ if (min_length > length) {
+ // this might happen for methods that take simd types, since those arguments don't show up in the
+ // method signature encoding at all, but they're still added to the frame size.
+ // fprintf (stderr, " min length: %i is higher than calculated length: %i for [%s %s] with description %s\n", min_length, length, class_getName (cls), sel_getName (sel), method_description);
+ length = min_length;
+ }
+ }
+
+ // we can't detect varargs, so just add 16 more pointer sized arguments to be on the safe-ish side.
+ length += sizeof (void *) * 16;
+
+ return length;
}
-#endif
static inline void
find_objc_method_implementation (struct objc_super *sup, id self, SEL sel, IMP xamarin_impl)
diff --git a/runtime/xamarin/main.h b/runtime/xamarin/main.h
index 28ac23e64fd8..4aa8b38e2921 100644
--- a/runtime/xamarin/main.h
+++ b/runtime/xamarin/main.h
@@ -19,6 +19,24 @@
extern "C" {
#endif
+/* This enum must always match the identical enum in src/ObjCRuntime/ExceptionMode.cs */
+enum MarshalObjectiveCExceptionMode : int {
+ MarshalObjectiveCExceptionModeDefault = 0,
+ MarshalObjectiveCExceptionModeUnwindManagedCode = 1,
+ MarshalObjectiveCExceptionModeThrowManagedException = 2,
+ MarshalObjectiveCExceptionModeAbort = 3,
+ MarshalObjectiveCExceptionModeDisable = 4,
+};
+
+/* This enum must always match the identical enum in src/ObjCRuntime/ExceptionMode.cs */
+enum MarshalManagedExceptionMode : int {
+ MarshalManagedExceptionModeDefault = 0,
+ MarshalManagedExceptionModeUnwindNativeCode = 1,
+ MarshalManagedExceptionModeThrowObjectiveCException = 2,
+ MarshalManagedExceptionModeAbort = 3,
+ MarshalManagedExceptionModeDisable = 4,
+};
+
extern bool mono_use_llvm; // this is defined inside mono
extern bool xamarin_use_new_assemblies;
@@ -31,6 +49,9 @@ extern bool xamarin_compact_seq_points;
extern int xamarin_log_level;
extern const char *xamarin_executable_name;
extern const char *xamarin_arch_name;
+extern bool xamarin_is_gc_coop;
+extern enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode;
+extern enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode;
#ifdef MONOTOUCH
extern NSString* xamarin_crashlytics_api_key;
diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h
index aa15dfb9af2d..208d2de5f7b7 100644
--- a/runtime/xamarin/runtime.h
+++ b/runtime/xamarin/runtime.h
@@ -144,6 +144,8 @@ void xamarin_set_gc_pump_enabled (bool value);
typedef void (*XamarinUnhandledExceptionFunc) (MonoObject *exc, const char *type_name, const char *message, const char *trace);
void xamarin_install_unhandled_exception_hook (XamarinUnhandledExceptionFunc func);
+void xamarin_process_nsexception (NSException *exc);
+void xamarin_process_managed_exception (MonoObject *exc);
id xamarin_invoke_objc_method_implementation (id self, SEL sel, IMP xamarin_impl);
diff --git a/src/ObjCRuntime/ExceptionMode.cs b/src/ObjCRuntime/ExceptionMode.cs
new file mode 100644
index 000000000000..557f3d58fe31
--- /dev/null
+++ b/src/ObjCRuntime/ExceptionMode.cs
@@ -0,0 +1,30 @@
+//
+// ExceptionMode.cs:
+//
+// Authors:
+// Rolf Bjarne Kvinge
+//
+// Copyright 2016 Xamarin Inc.
+
+using System;
+using XamCore.Foundation;
+
+namespace XamCore.ObjCRuntime {
+ /* This enum must always match the identical enum in runtime/xamarin/main.h */
+ public enum MarshalObjectiveCExceptionMode {
+ Default = 0,
+ UnwindManagedCode = 1, // not available for watchOS/COOP, default for the other platforms
+ ThrowManagedException = 2, // default for watchOS/COOP
+ Abort = 3,
+ Disable = 4, // this will also prevent the corresponding event from working
+ }
+
+ /* This enum must always match the identical enum in runtime/xamarin/main.h */
+ public enum MarshalManagedExceptionMode {
+ Default = 0,
+ UnwindNativeCode = 1, // not available for watchOS/COOP, default for the other platforms
+ ThrowObjectiveCException = 2, // default for watchOS/COOP
+ Abort = 3,
+ Disable = 4, // this will also prevent the corresponding event from working
+ }
+}
diff --git a/src/ObjCRuntime/Exceptions.cs b/src/ObjCRuntime/Exceptions.cs
new file mode 100644
index 000000000000..6125e4548d94
--- /dev/null
+++ b/src/ObjCRuntime/Exceptions.cs
@@ -0,0 +1,26 @@
+//
+// Exceptions.cs:
+//
+// Authors:
+// Rolf Bjarne Kvinge
+//
+// Copyright 2016 Xamarin Inc.
+
+using System;
+using XamCore.Foundation;
+
+namespace XamCore.ObjCRuntime {
+ public delegate void MarshalObjectiveCExceptionHandler (object sender, MarshalObjectiveCExceptionEventArgs args);
+
+ public class MarshalObjectiveCExceptionEventArgs {
+ public NSException Exception { get; set; }
+ public MarshalObjectiveCExceptionMode ExceptionMode { get; set; }
+ }
+
+ public delegate void MarshalManagedExceptionHandler (object sender, MarshalManagedExceptionEventArgs args);
+
+ public class MarshalManagedExceptionEventArgs {
+ public Exception Exception { get; set; }
+ public MarshalManagedExceptionMode ExceptionMode { get; set; }
+ }
+}
diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs
index eef00c57030b..a35408cc0982 100644
--- a/src/ObjCRuntime/Runtime.cs
+++ b/src/ObjCRuntime/Runtime.cs
@@ -110,6 +110,8 @@ internal unsafe struct InitializationOptions {
public Delegates Delegates;
public Trampolines Trampolines;
public RegistrationData *RegistrationData;
+ public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode;
+ public MarshalManagedExceptionMode MarshalManagedExceptionMode;
public bool UseOldDynamicRegistrar {
get {
@@ -209,6 +211,9 @@ unsafe static void Initialize (ref InitializationOptions options)
UseAutoreleasePoolInThreadPool = true;
#endif
+ objc_exception_mode = options.MarshalObjectiveCExceptionMode;
+ managed_exception_mode = options.MarshalManagedExceptionMode;
+
initialized = true;
#if PROFILE
Console.WriteLine ("Runtime.Initialize completed in {0} ms", watch.ElapsedMilliseconds);
@@ -234,6 +239,43 @@ static bool ThreadPoolDispatcher (Func callback)
}
#endif
+ static MarshalObjectiveCExceptionMode objc_exception_mode;
+ static MarshalManagedExceptionMode managed_exception_mode;
+
+ public static event MarshalObjectiveCExceptionHandler MarshalObjectiveCException;
+ public static event MarshalManagedExceptionHandler MarshalManagedException;
+
+ static MarshalObjectiveCExceptionMode OnMarshalObjectiveCException (IntPtr exception_handle)
+ {
+ if (MarshalObjectiveCException != null) {
+ var exception = GetNSObject (exception_handle);
+ var args = new MarshalObjectiveCExceptionEventArgs ()
+ {
+ Exception = exception,
+ ExceptionMode = objc_exception_mode,
+ };
+
+ MarshalObjectiveCException (null, args);
+ return args.ExceptionMode;
+ }
+ return objc_exception_mode;
+ }
+
+ static MarshalManagedExceptionMode OnMarshalManagedException (int exception_handle)
+ {
+ if (MarshalManagedException != null) {
+ var exception = GCHandle.FromIntPtr (new IntPtr (exception_handle)).Target as Exception;
+ var args = new MarshalManagedExceptionEventArgs ()
+ {
+ Exception = exception,
+ ExceptionMode = managed_exception_mode,
+ };
+ MarshalManagedException (null, args);
+ return args.ExceptionMode;
+ }
+ return managed_exception_mode;
+ }
+
#if !COREBUILD && (XAMARIN_APPLETLS || XAMARIN_NO_TLS)
// This method is rewritten by the linker in CoreTlsProviderStep.
static MonoTlsProvider TlsProviderFactoryCallback ()
@@ -273,6 +315,32 @@ static void ThrowNSException (IntPtr ns_exception)
#endif
}
+ static int CreateNSException (IntPtr ns_exception)
+ {
+ Exception ex;
+#if MONOMAC
+ ex = new ObjCException (Runtime.GetNSObject (ns_exception));
+#else
+ ex = new MonoTouchException (Runtime.GetNSObject (ns_exception));
+#endif
+ return GCHandle.ToIntPtr (GCHandle.Alloc (ex)).ToInt32 ();
+ }
+
+ static IntPtr UnwrapNSException (int exc_handle)
+ {
+ var obj = GCHandle.FromIntPtr (new IntPtr (exc_handle)).Target;
+#if MONOMAC
+ var exc = obj as ObjCException;
+#else
+ var exc = obj as MonoTouchException;
+#endif
+ if (exc != null) {
+ return exc.NSException.DangerousRetain ().DangerousAutorelease ().Handle;
+ } else {
+ return IntPtr.Zero;
+ }
+ }
+
static IntPtr GetBlockWrapperCreator (IntPtr method, int parameter)
{
return ObjectWrapper.Convert (GetBlockWrapperCreator ((MethodInfo) ObjectWrapper.Convert (method), parameter));
diff --git a/src/frameworks.sources b/src/frameworks.sources
index b97fd8caa61d..83a2e59b4575 100644
--- a/src/frameworks.sources
+++ b/src/frameworks.sources
@@ -1393,6 +1393,8 @@ SHARED_SOURCES = \
ObjCRuntime/Dlfcn.cs \
ObjCRuntime/DynamicRegistrar.cs \
ObjCRuntime/ErrorHelper.cs \
+ ObjCRuntime/Exceptions.cs \
+ ObjCRuntime/ExceptionMode.cs \
ObjCRuntime/IDynamicRegistrar.cs \
ObjCRuntime/Method.cs \
ObjCRuntime/MethodDescription.cs \
diff --git a/tests/bindings-test/ApiDefinition.cs b/tests/bindings-test/ApiDefinition.cs
index 30784ff83544..eac8f11564dd 100644
--- a/tests/bindings-test/ApiDefinition.cs
+++ b/tests/bindings-test/ApiDefinition.cs
@@ -202,6 +202,25 @@ interface ObjCRegistrarTest {
[Export ("testBlocks")]
bool TestBlocks ();
+
+ }
+
+ [BaseType (typeof (NSObject))]
+ interface ObjCExceptionTest {
+ [Export ("throwObjCException")]
+ void ThrowObjCException ();
+
+ [Export ("throwManagedException")]
+ void ThrowManagedException ();
+
+ [Export ("invokeManagedExceptionThrower")]
+ void InvokeManagedExceptionThrower ();
+
+ [Export ("invokeManagedExceptionThrowerAndRethrow")]
+ void InvokeManagedExceptionThrowerAndRethrow ();
+
+ [Export ("invokeManagedExceptionThrowerAndCatch")]
+ void InvokeManagedExceptionThrowerAndCatch ();
}
[BaseType (typeof (NSObject))]
diff --git a/tests/test-libraries/libtest.h b/tests/test-libraries/libtest.h
index 746506bdb7fb..a9c34cdabc29 100644
--- a/tests/test-libraries/libtest.h
+++ b/tests/test-libraries/libtest.h
@@ -111,6 +111,18 @@ typedef unsigned int (^RegistrarTestBlock) (unsigned int magic);
-(bool) testBlocks;
@end
+/*
+ * ObjC test class used for exception tests.
+ */
+@interface ObjCExceptionTest : NSObject {
+}
+ -(void) throwObjCException;
+ -(void) throwManagedException;
+ -(void) invokeManagedExceptionThrower;
+ -(void) invokeManagedExceptionThrowerAndRethrow;
+ -(void) invokeManagedExceptionThrowerAndCatch;
+@end
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tests/test-libraries/libtest.m b/tests/test-libraries/libtest.m
index 14fd264d52b7..7c85a121602c 100644
--- a/tests/test-libraries/libtest.m
+++ b/tests/test-libraries/libtest.m
@@ -187,6 +187,42 @@ -(bool) testBlocks
}
@end
+@implementation ObjCExceptionTest
+{
+}
+ -(void) throwObjCException
+ {
+ [NSException raise:@"Some exception" format:@"exception was thrown"];
+ }
+
+ -(void) throwManagedException
+ {
+ abort (); // this method should be overridden in managed code.
+ }
+
+ -(void) invokeManagedExceptionThrower
+ {
+ [self throwManagedException];
+ }
+
+ -(void) invokeManagedExceptionThrowerAndRethrow
+ {
+ @try {
+ [self throwManagedException];
+ } @catch (id exc) {
+ [NSException raise:@"Caught exception" format:@"exception was rethrown"];
+ }
+ }
+ -(void) invokeManagedExceptionThrowerAndCatch
+ {
+ @try {
+ [self throwManagedException];
+ } @catch (id exc) {
+ // do nothing
+ }
+ }
+@end
+
@interface CtorChaining1 : NSObject
@property BOOL initCalled;
@property BOOL initCallsInitCalled;
diff --git a/tools/common/Application.cs b/tools/common/Application.cs
index 697252c41fde..7239a2b2d4c5 100644
--- a/tools/common/Application.cs
+++ b/tools/common/Application.cs
@@ -9,6 +9,8 @@
using Xamarin.Utils;
+using XamCore.ObjCRuntime;
+
namespace Xamarin.Bundler {
[Flags]
@@ -43,6 +45,23 @@ public partial class Application
public List Definitions = new List ();
public Mono.Linker.I18nAssemblies I18n;
+ public bool? EnableCoopGC;
+ public MarshalObjectiveCExceptionMode MarshalObjectiveCExceptions;
+ public MarshalManagedExceptionMode MarshalManagedExceptions;
+
+ public bool RequiresPInvokeWrappers {
+ get {
+#if MTOUCH
+ if (IsSimulatorBuild)
+ return false;
+#else
+ if (Driver.Is64Bit)
+ return false;
+#endif
+ return MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.ThrowManagedException || MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Abort;
+ }
+ }
+
public string PlatformName {
get {
switch (Platform) {
@@ -329,5 +348,48 @@ public static void TryDelete (string path)
}
}
+ public void InitializeCommon ()
+ {
+ if (Platform == ApplePlatform.WatchOS && EnableCoopGC.HasValue && !EnableCoopGC.Value)
+ throw ErrorHelper.CreateError (88, "The GC must be in cooperative mode for watchOS apps. Please remove the --coop:false argument to mtouch.");
+
+ if (!EnableCoopGC.HasValue)
+ EnableCoopGC = Platform == ApplePlatform.WatchOS;
+
+ if (EnableCoopGC.Value) {
+ switch (MarshalObjectiveCExceptions) {
+ case MarshalObjectiveCExceptionMode.UnwindManagedCode:
+ case MarshalObjectiveCExceptionMode.Disable:
+ throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC.", "--marshal-objectivec-exceptions", MarshalObjectiveCExceptions.ToString ().ToLowerInvariant ());
+ }
+ switch (MarshalManagedExceptions) {
+ case MarshalManagedExceptionMode.UnwindNativeCode:
+ case MarshalManagedExceptionMode.Disable:
+ throw ErrorHelper.CreateError (89, "The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC.", "--marshal-managed-exceptions", MarshalManagedExceptions.ToString ().ToLowerInvariant ());
+ }
+ }
+
+
+ bool isSimulatorOrDesktopDebug = EnableDebug;
+#if MTOUCH
+ isSimulatorOrDesktopDebug &= IsSimulatorBuild;
+#endif
+
+ if (MarshalObjectiveCExceptions == MarshalObjectiveCExceptionMode.Default) {
+ if (EnableCoopGC.Value) {
+ MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException;
+ } else {
+ MarshalObjectiveCExceptions = isSimulatorOrDesktopDebug ? MarshalObjectiveCExceptionMode.UnwindManagedCode : MarshalObjectiveCExceptionMode.Disable;
+ }
+ }
+
+ if (MarshalManagedExceptions == MarshalManagedExceptionMode.Default) {
+ if (EnableCoopGC.Value) {
+ MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException;
+ } else {
+ MarshalManagedExceptions = isSimulatorOrDesktopDebug ? MarshalManagedExceptionMode.UnwindNativeCode : MarshalManagedExceptionMode.Disable;
+ }
+ }
+ }
}
}
diff --git a/tools/common/Driver.cs b/tools/common/Driver.cs
index 6c10dca2a80d..ec9dbf27d6e6 100644
--- a/tools/common/Driver.cs
+++ b/tools/common/Driver.cs
@@ -14,9 +14,61 @@
using System.Text;
using Xamarin.Utils;
+using XamCore.ObjCRuntime;
namespace Xamarin.Bundler {
public partial class Driver {
+ static void AddSharedOptions (Mono.Options.OptionSet options)
+ {
+ options.Add ("coop:", "If the GC should run in cooperative mode.", v => { App.EnableCoopGC = ParseBool (v, "coop"); }, hidden: true);
+ options.Add ("marshal-objectivec-exceptions:", v => {
+ switch (v) {
+ case "default":
+ App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Default;
+ break;
+ case "unwindmanaged":
+ case "unwindmanagedcode":
+ App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.UnwindManagedCode;
+ break;
+ case "throwmanaged":
+ case "throwmanagedexception":
+ App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.ThrowManagedException;
+ break;
+ case "abort":
+ App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Abort;
+ break;
+ case "disable":
+ App.MarshalObjectiveCExceptions = MarshalObjectiveCExceptionMode.Disable;
+ break;
+ default:
+ throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-objective-exceptions", "Invalid value: " + v);
+ }
+ });
+ options.Add ("marshal-managed-exceptions:", v => {
+ switch (v) {
+ case "default":
+ App.MarshalManagedExceptions = MarshalManagedExceptionMode.Default;
+ break;
+ case "unwindnative":
+ case "unwindnativecode":
+ App.MarshalManagedExceptions = MarshalManagedExceptionMode.UnwindNativeCode;
+ break;
+ case "throwobjectivec":
+ case "throwobjectivecexception":
+ App.MarshalManagedExceptions = MarshalManagedExceptionMode.ThrowObjectiveCException;
+ break;
+ case "abort":
+ App.MarshalManagedExceptions = MarshalManagedExceptionMode.Abort;
+ break;
+ case "disable":
+ App.MarshalManagedExceptions = MarshalManagedExceptionMode.Disable;
+ break;
+ default:
+ throw ErrorHelper.CreateError (26, "Could not parse the command line argument '{0}': {1}", "--marshal-managed-exceptions", "Invalid value: " + v);
+ }
+ });
+ }
+
#if MONOMAC
#pragma warning disable 0414
static string userTargetFramework = TargetFramework.Default.ToString ();
@@ -144,6 +196,30 @@ static void FileMove (string source, string target)
File.Move (source, target);
}
+ static void MoveIfDifferent (string path, string tmp)
+ {
+ // Don't read the entire file into memory, it can be quite big in certain cases.
+
+ bool move = false;
+
+ using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) {
+ using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) {
+ if (fs1.Length != fs2.Length) {
+ Log (3, "New file '{0}' has different length, writing new file.", path);
+ move = true;
+ } else {
+ move = !Cache.CompareStreams (fs1, fs2);
+ }
+ }
+ }
+
+ if (move) {
+ FileMove (tmp, path);
+ } else {
+ Log (3, "Target {0} is up-to-date.", path);
+ }
+ }
+
public static void WriteIfDifferent (string path, string contents)
{
var tmp = path + ".tmp";
@@ -155,29 +231,31 @@ public static void WriteIfDifferent (string path, string contents)
return;
}
- // Don't read the entire file into memory, it can be quite big in certain cases.
-
- bool move = false;
File.WriteAllText (tmp, contents);
+ MoveIfDifferent (path, tmp);
+ } catch (Exception e) {
+ File.WriteAllText (path, contents);
+ ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
+ } finally {
+ Application.TryDelete (tmp);
+ }
+ }
- using (var fs1 = new FileStream (path, FileMode.Open, FileAccess.Read)) {
- using (var fs2 = new FileStream (tmp, FileMode.Open, FileAccess.Read)) {
- if (fs1.Length != fs2.Length) {
- Log (3, "New file '{0}' has different length, writing new file.", path);
- move = true;
- } else {
- move = !Cache.CompareStreams (fs1, fs2);
- }
- }
- }
+ public static void WriteIfDifferent (string path, byte[] contents)
+ {
+ var tmp = path + ".tmp";
- if (move) {
- FileMove (tmp, path);
- } else {
- Log (3, "Target {0} is up-to-date.", path);
+ try {
+ if (!File.Exists (path)) {
+ File.WriteAllBytes (path, contents);
+ Log (3, "File '{0}' does not exist, creating it.", path);
+ return;
}
+
+ File.WriteAllBytes (tmp, contents);
+ MoveIfDifferent (path, tmp);
} catch (Exception e) {
- File.WriteAllText (path, contents);
+ File.WriteAllBytes (path, contents);
ErrorHelper.Warning (1014, e, "Failed to re-use cached version of '{0}': {1}.", path, e.Message);
} finally {
Application.TryDelete (tmp);
diff --git a/tools/common/PInvokeWrapperGenerator.cs b/tools/common/PInvokeWrapperGenerator.cs
new file mode 100644
index 000000000000..800f5ceebb4e
--- /dev/null
+++ b/tools/common/PInvokeWrapperGenerator.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using Mono.Cecil;
+
+using Xamarin.Bundler;
+using XamCore.Registrar;
+
+namespace Xamarin.Bundler
+{
+ class PInvokeWrapperGenerator
+ {
+ public Dictionary signatures = new Dictionary ();
+ public List exceptions = new List ();
+ public StringBuilder signature = new StringBuilder ();
+ public HashSet names = new HashSet ();
+
+ public AutoIndentStringBuilder sb = new AutoIndentStringBuilder ();
+ AutoIndentStringBuilder hdr = new AutoIndentStringBuilder ();
+ AutoIndentStringBuilder decls = new AutoIndentStringBuilder ();
+ AutoIndentStringBuilder mthds = new AutoIndentStringBuilder ();
+
+ public StaticRegistrar Registrar;
+ public string HeaderPath;
+ public string SourcePath;
+
+ bool first;
+
+ public void Start ()
+ {
+ if (Driver.EnableDebug)
+ hdr.WriteLine ("#define DEBUG 1");
+
+ hdr.WriteLine ("#include ");
+ hdr.WriteLine ("#include ");
+ hdr.WriteLine ("#include ");
+ hdr.WriteLine ("#include ");
+ hdr.WriteLine ("#include ");
+
+ Registrar.GeneratePInvokeWrappersStart (hdr, decls, mthds);
+
+ mthds.WriteLine ($"#include \"{Path.GetFileName (HeaderPath)}\"");
+
+ sb.WriteLine ("extern \"C\" {");
+ }
+
+ public void End ()
+ {
+ sb.WriteLine ("}");
+
+ Registrar.GeneratePInvokeWrappersEnd ();
+
+ Driver.WriteIfDifferent (HeaderPath, hdr.ToString () + "\n" + decls.ToString () + "\n");
+ Driver.WriteIfDifferent (SourcePath, mthds.ToString () + "\n" + sb.ToString () + "\n");
+ }
+
+ public void ProcessMethod (MethodDefinition method)
+ {
+ if (!first) {
+ Start ();
+ first = true;
+ }
+
+ Registrar.GeneratePInvokeWrapper (this, method);
+ }
+ }
+}
+
diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs
index 87d8d0b10838..13a8202853a6 100644
--- a/tools/common/StaticRegistrar.cs
+++ b/tools/common/StaticRegistrar.cs
@@ -498,45 +498,53 @@ public static bool IsNativeObject (TypeReference tr)
}
}
- class StaticRegistrar : Registrar {
- bool is_simulator;
- bool? is_64_bits;
+ public interface IStaticRegistrar
+ {
+ void Generate (IEnumerable assemblies, string header_path, string source_path);
+ void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string assembly);
+ Mono.Linker.LinkContext LinkContext { get; set; }
+ }
+
+ class StaticRegistrar : Registrar, IStaticRegistrar {
+ public Application App { get; private set; }
+ public Target Target { get; private set; }
+ public bool IsSingleAssembly { get { return !string.IsNullOrEmpty (single_assembly); } }
+
string single_assembly;
IEnumerable input_assemblies;
+ Mono.Linker.LinkContext link_context;
Dictionary availability_annotations;
- StaticRegistrar (Application app)
+ public Mono.Linker.LinkContext LinkContext {
+ get {
+ return link_context;
+ }
+ set {
+ link_context = value;
+ availability_annotations = link_context?.Annotations.GetCustomAnnotations ("Availability");
+ }
+ }
+
+ void Init (Application app)
{
+ this.App = app;
trace = !LaxMode && (app.RegistrarOptions & RegistrarOptions.Trace) == RegistrarOptions.Trace;
}
- public StaticRegistrar (Application app, IEnumerable assemblies, bool is_simulator, string single_assembly = null)
- : this (app)
+ public StaticRegistrar (Application app)
{
- this.is_simulator = is_simulator;
- this.single_assembly = single_assembly;
- this.input_assemblies = assemblies;
-
- foreach (var assembly in assemblies)
- RegisterAssembly (assembly);
+ Init (app);
}
- public StaticRegistrar (Application app, IEnumerable assemblies, bool is_simulator, bool is_64_bits, Mono.Linker.LinkContext link_context)
- : this (app)
+ public StaticRegistrar (Target target)
{
- this.is_simulator = is_simulator;
- this.is_64_bits = is_64_bits;
- this.input_assemblies = assemblies;
-
- availability_annotations = link_context?.Annotations.GetCustomAnnotations ("Availability");
-
- foreach (var assembly in assemblies)
- RegisterAssembly (assembly);
+ Init (target.App);
+ this.Target = target;
}
protected override bool LaxMode {
get {
- return !string.IsNullOrEmpty (single_assembly);
+ return IsSingleAssembly;
}
}
@@ -606,19 +614,27 @@ protected override bool HasThisAttribute (MethodDefinition method)
return SharedStatic.TryGetAttributeImpl (method, "System.Runtime.CompilerServices", "ExtensionAttribute", out attrib);
}
+#if MTOUCH
+ public bool IsSimulator {
+ get { return App.IsSimulatorBuild; }
+ }
+#endif
+
protected override bool IsSimulatorOrDesktop {
get {
#if MONOMAC
return true;
#else
- return is_simulator;
+ return App.IsSimulatorBuild;
#endif
}
}
protected override bool Is64Bits {
get {
- return is_64_bits.Value;
+ if (IsSingleAssembly)
+ throw new InvalidOperationException ("Can't emit size-specific code in single assembly mode.");
+ return Target.Is64Build;
}
}
@@ -1735,8 +1751,10 @@ void CheckNamespace (TypeReference type, List exceptions)
case "CoreAudioKit":
// fatal error: 'CoreAudioKit/CoreAudioKit.h' file not found
// radar filed with Apple - but that framework / header is not yet shipped with the iOS SDK simulator
- if (is_simulator)
+#if MTOUCH
+ if (IsSimulator)
return;
+#endif
goto default;
case "Metal":
case "MetalKit":
@@ -1744,7 +1762,7 @@ void CheckNamespace (TypeReference type, List exceptions)
// #error Metal Simulator is currently unsupported
// this framework is _officially_ not available on the simulator (in iOS8)
#if !MONOMAC
- if (is_simulator)
+ if (IsSimulator)
return;
#endif
goto default;
@@ -3036,10 +3054,16 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List
invoke.AppendLine ("xamarin_set_nsobject_flags (mthis, flags);");
}
+ var marshal_exception = "NULL";
+ if (App.MarshalManagedExceptions != MarshalManagedExceptionMode.Disable) {
+ invoke.AppendLine ("MonoObject *exception = NULL;");
+ marshal_exception = "&exception";
+ }
+
if (!isVoid)
invoke.AppendFormat ("{0} retval = ", "MonoObject *");
- invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, NULL);", isStatic ? "NULL" : "mthis");
+ invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, {1});", isStatic ? "NULL" : "mthis", marshal_exception);
if (isCtor)
invoke.AppendLine ("xamarin_create_managed_ref (self, mthis, true);");
@@ -3234,6 +3258,9 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List
if (trace )
body.AppendLine (nslog_end);
+ if (App.MarshalManagedExceptions != MarshalManagedExceptionMode.Disable)
+ body.WriteLine ("xamarin_process_managed_exception (exception);");
+
if (isCtor) {
body.WriteLine ("return self;");
} else if (isVoid) {
@@ -3352,24 +3379,149 @@ public override bool Equals (object obj)
}
}
-#if MONOMAC
- public static string Generate (Application app, IEnumerable list, bool is_64_bits, Mono.Linker.LinkContext link_context)
+ public void GeneratePInvokeWrappersStart (AutoIndentStringBuilder hdr, AutoIndentStringBuilder decls, AutoIndentStringBuilder mthds)
{
- return new StaticRegistrar (app, list, true, is_64_bits, link_context).Generate ();
+ header = hdr;
+ declarations = decls;
+ methods = mthds;
}
-#else
- public static string Generate (Application app, IEnumerable list, bool simulator, bool is_64_bits, Mono.Linker.LinkContext link_context)
+
+ public void GeneratePInvokeWrappersEnd ()
{
- return new StaticRegistrar (app, list, simulator, is_64_bits, link_context).Generate ();
+ header = null;
+ declarations = null;
+ methods = null;
+ namespaces.Clear ();
+ structures.Clear ();
+
+ FlushTrace ();
}
- public static string Generate (Application app, IEnumerable list, bool simulator, string single_assembly = null)
+ public void GeneratePInvokeWrapper (PInvokeWrapperGenerator state, MethodDefinition method)
{
- return new StaticRegistrar (app, list, simulator, single_assembly).Generate ();
+ var signatures = state.signatures;
+ var exceptions = state.exceptions;
+ var signature = state.signature;
+ var names = state.names;
+ var sb = state.sb;
+ var pinfo = method.PInvokeInfo;
+ var is_stret = pinfo.EntryPoint.EndsWith ("_stret");
+ var isVoid = method.ReturnType.FullName == "System.Void";
+ var descriptiveMethodName = method.DeclaringType.FullName + "." + method.Name;
+
+ signature.Clear ();
+
+ string native_return_type;
+ int first_parameter = 0;
+
+ if (is_stret) {
+ native_return_type = ToObjCParameterType (method.Parameters [0].ParameterType.GetElementType (), descriptiveMethodName, exceptions, method);
+ first_parameter = 1;
+ } else {
+ native_return_type = ToObjCParameterType (method.ReturnType, descriptiveMethodName, exceptions, method);
+ }
+
+ signature.Append (native_return_type);
+ signature.Append (" ");
+ signature.Append (pinfo.EntryPoint);
+ signature.Append (" (");
+ for (int i = 0; i < method.Parameters.Count; i++) {
+ if (i > 0)
+ signature.Append (", ");
+ signature.Append (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method));
+ }
+ signature.Append (")");
+
+ string wrapperName;
+ if (!signatures.TryGetValue (signature.ToString (), out wrapperName)) {
+ var name = "xamarin_pinvoke_wrapper_" + method.Name;
+ var counter = 0;
+ while (names.Contains (name)) {
+ name = "xamarin_pinvoke_wrapper_" + method.Name + (++counter).ToString ();
+ }
+ names.Add (name);
+ signatures [signature.ToString ()] = wrapperName = name;
+
+ sb.WriteLine ("// EntryPoint: {0}", pinfo.EntryPoint);
+ sb.WriteLine ("// Managed method: {0}.{1}", method.DeclaringType.FullName, method.Name);
+ sb.WriteLine ("// Signature: {0}", signature.ToString ());
+
+ sb.Write ("typedef ");
+ sb.Write (native_return_type);
+ sb.Write ("(*func_");
+ sb.Write (name);
+ sb.Write (") (");
+ for (int i = first_parameter; i < method.Parameters.Count; i++) {
+ if (i > first_parameter)
+ sb.Write (", ");
+ sb.Write (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method));
+ sb.Write (" ");
+ sb.Write (method.Parameters[i].Name);
+ }
+ sb.WriteLine (");");
+
+ sb.WriteLine (native_return_type);
+ sb.Write (name);
+ sb.Write (" (", method.Name);
+ for (int i = first_parameter; i < method.Parameters.Count; i++) {
+ if (i > first_parameter)
+ sb.Write (", ");
+ sb.Write (ToObjCParameterType (method.Parameters[i].ParameterType, descriptiveMethodName, exceptions, method));
+ sb.Write (" ");
+ sb.Write (method.Parameters [i].Name);
+ }
+ sb.WriteLine (")");
+ sb.WriteLine ("{");
+ sb.WriteLine ("@try {");
+ if (!isVoid || is_stret)
+ sb.Write ("return ");
+ sb.Write ("((func_{0}) {1}) (", name, pinfo.EntryPoint);
+ for (int i = first_parameter; i < method.Parameters.Count; i++) {
+ if (i > first_parameter)
+ sb.Write (", ");
+ sb.Write (method.Parameters [i].Name);
+ }
+ sb.WriteLine (");");
+ sb.WriteLine ("} @catch (NSException *exc) {");
+ sb.WriteLine ("xamarin_process_nsexception (exc);");
+ sb.WriteLine ("}");
+ sb.WriteLine ("}");
+ sb.WriteLine ();
+ } else {
+ Console.WriteLine ("Signature already processed: {0} for {1}.{2}", signature.ToString (), method.DeclaringType.FullName, method.Name);
+ }
+
+ // find the module reference to __Internal
+ ModuleReference mr = null;
+ foreach (var mref in method.Module.ModuleReferences) {
+ if (mref.Name == "__Internal") {
+ mr = mref;
+ break;
+ }
+ }
+ if (mr == null)
+ method.Module.ModuleReferences.Add (mr = new ModuleReference ("__Internal"));
+ pinfo.Module = mr;
+ pinfo.EntryPoint = wrapperName;
+ }
+
+ public void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string assembly)
+ {
+ single_assembly = assembly;
+ Generate (assemblies, header_path, source_path);
+ }
+
+ public void Generate (IEnumerable assemblies, string header_path, string source_path)
+ {
+ this.input_assemblies = assemblies;
+
+ foreach (var assembly in assemblies)
+ RegisterAssembly (assembly);
+
+ Generate (header_path, source_path);
}
-#endif
- public string Generate ()
+ void Generate (string header_path, string source_path)
{
using (var sb = new AutoIndentStringBuilder ()) {
using (var hdr = new AutoIndentStringBuilder ()) {
@@ -3391,6 +3543,8 @@ public string Generate ()
declarations = decls;
methods = mthds;
+ mthds.WriteLine ($"#include \"{Path.GetFileName (header_path)}\"");
+
Specialize (sb);
header = null;
@@ -3399,7 +3553,8 @@ public string Generate ()
FlushTrace ();
- return hdr.ToString () + "\n" + decls.ToString () + "\n" + mthds.ToString () + "\n" + sb.ToString ();
+ Driver.WriteIfDifferent (header_path, hdr.ToString () + "\n" + decls.ToString () + "\n");
+ Driver.WriteIfDifferent (source_path, mthds.ToString () + "\n" + sb.ToString () + "\n");
}
}
}
diff --git a/tools/common/Target.cs b/tools/common/Target.cs
index bdfd5e32ce98..027b0159bf36 100644
--- a/tools/common/Target.cs
+++ b/tools/common/Target.cs
@@ -18,6 +18,7 @@
using Xamarin.Linker;
using Xamarin.Utils;
+using XamCore.Registrar;
#if MONOTOUCH
using MonoTouch;
@@ -36,11 +37,19 @@ public partial class Target {
public List Assemblies = new List ();
public PlatformLinkContext LinkContext;
+ public LinkerOptions LinkerOptions;
public PlatformResolver Resolver = new PlatformResolver ();
public HashSet Frameworks = new HashSet ();
public HashSet WeakFrameworks = new HashSet ();
+ public IStaticRegistrar StaticRegistrar { get; set; }
+
+#if MONOMAC
+ public bool Is32Build { get { return !Driver.Is64Bit; } }
+ public bool Is64Build { get { return Driver.Is64Bit; } }
+#endif
+
public Target (Application app)
{
this.App = app;
diff --git a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs
index f1e780198a30..bcec3b380847 100644
--- a/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs
+++ b/tools/linker/MonoTouch.Tuner/ListExportedSymbols.cs
@@ -5,6 +5,8 @@
using Mono.Linker;
using Mono.Linker.Steps;
using Mono.Tuner;
+
+using Xamarin.Bundler;
using Xamarin.Linker;
#if MONOMAC
@@ -17,6 +19,13 @@ namespace MonoTouch.Tuner
{
public class ListExportedSymbols : BaseStep
{
+ PInvokeWrapperGenerator state;
+
+ internal ListExportedSymbols (PInvokeWrapperGenerator state)
+ {
+ this.state = state;
+ }
+
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
base.ProcessAssembly (assembly);
@@ -55,8 +64,25 @@ void ProcessType (TypeDefinition type)
void ProcessMethod (MethodDefinition method)
{
- if (method.IsPInvokeImpl && method.HasPInvokeInfo && method.PInvokeInfo.Module.Name == "__Internal")
- ((DerivedLinkContext) Context).RequiredSymbols[method.PInvokeInfo.EntryPoint] = method;
+ if (method.IsPInvokeImpl && method.HasPInvokeInfo) {
+ var pinfo = method.PInvokeInfo;
+ if (pinfo.Module.Name == "__Internal")
+ ((DerivedLinkContext) Context).RequiredSymbols [pinfo.EntryPoint] = method;
+
+ if (state != null) {
+ switch (pinfo.EntryPoint) {
+ case "objc_msgSend":
+ case "objc_msgSendSuper":
+ case "objc_msgSend_stret":
+ case "objc_msgSendSuper_stret":
+ case "objc_msgSend_fpret":
+ state.ProcessMethod (method);
+ break;
+ default:
+ return;
+ }
+ }
+ }
if (MarkStep.IsPropertyMethod (method)) {
var property = MarkStep.GetProperty (method);
diff --git a/tools/mmp/Makefile b/tools/mmp/Makefile
index d2b5e599261b..fd5157bacb1d 100644
--- a/tools/mmp/Makefile
+++ b/tools/mmp/Makefile
@@ -97,10 +97,12 @@ mmp_sources = \
$(TOP)/tools/common/cache.cs \
driver.cs \
$(TOP)/src/ObjCRuntime/ErrorHelper.cs \
+ $(TOP)/src/ObjCRuntime/ExceptionMode.cs \
error.cs \
resolver.cs \
$(MONO_PATH)/mcs/class/Mono.Options/Mono.Options/Options.cs \
Tuning.cs \
+ $(TOP)/tools/common/PInvokeWrapperGenerator.cs \
$(TOP)/tools/common/TargetFramework.cs \
$(TOP)/tools/common/Driver.cs \
$(TOP)/tools/common/Frameworks.cs \
diff --git a/tools/mmp/Tuning.cs b/tools/mmp/Tuning.cs
index 80164970916c..4e284e92a757 100644
--- a/tools/mmp/Tuning.cs
+++ b/tools/mmp/Tuning.cs
@@ -30,6 +30,7 @@ public class LinkerOptions {
public IList ExtraDefinitions { get; set; }
public TargetFramework TargetFramework { get; set; }
public string Architecture { get; set; }
+ internal PInvokeWrapperGenerator MarshalNativeExceptionsState { get; set; }
internal RuntimeOptions RuntimeOptions { get; set; }
public static I18nAssemblies ParseI18nAssemblies (string i18n)
@@ -56,6 +57,7 @@ public class MonoMacLinkContext : LinkContext {
Dictionary> pinvokes = new Dictionary> ();
public Dictionary RequiredSymbols = new Dictionary ();
+ List marshal_exception_pinvokes;
public MonoMacLinkContext (Pipeline pipeline, AssemblyResolver resolver) : base (pipeline, resolver)
{
@@ -64,6 +66,14 @@ public MonoMacLinkContext (Pipeline pipeline, AssemblyResolver resolver) : base
public IDictionary> PInvokeModules {
get { return pinvokes; }
}
+
+ public List MarshalExceptionPInvokes {
+ get {
+ if (marshal_exception_pinvokes == null)
+ marshal_exception_pinvokes = new List ();
+ return marshal_exception_pinvokes;
+ }
+ }
}
class Linker {
@@ -168,7 +178,7 @@ static Pipeline CreatePipeline (LinkerOptions options)
pipeline.AppendStep (new RegenerateGuidStep ());
}
- pipeline.AppendStep (new ListExportedSymbols ());
+ pipeline.AppendStep (new ListExportedSymbols (options.MarshalNativeExceptionsState));
pipeline.AppendStep (new OutputStep ());
diff --git a/tools/mmp/driver.cs b/tools/mmp/driver.cs
index 9342d76afaba..306393d03116 100644
--- a/tools/mmp/driver.cs
+++ b/tools/mmp/driver.cs
@@ -48,6 +48,8 @@
using MonoMac.Tuner;
using Xamarin.Utils;
using Xamarin.Linker;
+using XamCore.Registrar;
+using XamCore.ObjCRuntime;
namespace Xamarin.Bundler {
public enum RegistrarMode {
@@ -78,6 +80,7 @@ public static partial class Driver {
static bool? profiling = false;
static bool? thread_check = null;
static string link_flags = null;
+ static LinkerOptions linker_options;
static bool arch_set = false;
static string arch = "i386";
@@ -280,6 +283,8 @@ static void Main2 (string [] args)
{ "http-message-handler=", "Specify the default HTTP Message Handler", v => { http_message_provider = v; }},
};
+ AddSharedOptions (os);
+
IList unprocessed;
try {
unprocessed = os.Parse (args);
@@ -378,6 +383,8 @@ static void Main2 (string [] args)
if (!IsUnifiedMobile && tls_provider != null)
throw new MonoMacException (2011, true, "Selecting a TLS Provider is only supported in the Unified Mobile profile");
+ App.InitializeCommon ();
+
Log ("Xamarin.Mac {0}{1}", Constants.Version, verbose > 0 ? "." + Constants.Revision : string.Empty);
if (verbose > 0)
@@ -574,6 +581,8 @@ static void Pack (IList unprocessed)
ExtractNativeLinkInfo ();
+ BuildTarget.StaticRegistrar = new StaticRegistrar (BuildTarget);
+
if (!no_executable) {
foreach (var nr in native_references) {
if (!native_libs.ContainsKey (nr))
@@ -596,6 +605,12 @@ static void Pack (IList unprocessed)
Watch (string.Format ("Linking (mode: '{0}')", App.LinkMode), 1);
}
+ if (App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers && BuildTarget.Is64Build) {
+ internalSymbols.Add ("xamarin_dyn_objc_msgSend");
+ internalSymbols.Add ("xamarin_dyn_objc_msgSendSuper");
+ internalSymbols.Add ("xamarin_dyn_objc_msgSend_stret");
+ internalSymbols.Add ("xamarin_dyn_objc_msgSendSuper_stret");
+ }
CopyDependencies (native_libs);
Watch ("Copy Dependencies", 1);
@@ -873,6 +888,8 @@ static string GenerateMain ()
sw.WriteLine ("\txamarin_custom_bundle_name = @\"" + custom_bundle_name + "\";");
}
sw.WriteLine ("\txamarin_use_il_registrar = {0};", registrar == RegistrarMode.IL ? "true" : "false");
+ sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", App.MarshalManagedExceptions);
+ sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", App.MarshalObjectiveCExceptions);
sw.WriteLine ();
if (Driver.registrar == RegistrarMode.Static)
sw.WriteLine ("\txamarin_create_classes ();");
@@ -920,9 +937,10 @@ static int Compile (IEnumerable internalSymbols)
SetSDKVersion ();
if (registrar == RegistrarMode.Static) {
- var code = XamCore.Registrar.StaticRegistrar.Generate (App, BuildTarget.Resolver.ResolverCache.Values, Is64Bit, BuildTarget.LinkContext);
registrarPath = Path.Combine (Cache.Location, "registrar.m");
- File.WriteAllText (registrarPath, code);
+ var registrarH = Path.Combine (Cache.Location, "registrar.h");
+ BuildTarget.StaticRegistrar.LinkContext = BuildTarget.LinkContext;
+ BuildTarget.StaticRegistrar.Generate (BuildTarget.Resolver.ResolverCache.Values, registrarH, registrarPath);
var platform_assembly = BuildTarget.Resolver.ResolverCache.First ((v) => v.Value.Name.Name == XamCore.Registrar.Registrar.PlatformAssembly).Value;
Frameworks.Gather (platform_assembly, BuildTarget.Frameworks, BuildTarget.WeakFrameworks);
@@ -1051,6 +1069,12 @@ static int Compile (IEnumerable internalSymbols)
if (!string.IsNullOrEmpty (DeveloperDirectory))
args.Append ("-isysroot ").Append (Quote (Path.Combine (DeveloperDirectory, "Platforms", "MacOSX.platform", "Developer", "SDKs", "MacOSX" + sdk_version + ".sdk"))).Append (' ');
+ if (App.RequiresPInvokeWrappers) {
+ var state = linker_options.MarshalNativeExceptionsState;
+ state.End ();
+ args.Append (Quote (state.SourcePath)).Append (' ');
+ }
+
var main = Path.Combine (Cache.Location, "main.m");
File.WriteAllText (main, mainSource);
args.Append (Quote (main));
@@ -1142,8 +1166,16 @@ static IDictionary> Link ()
// by default we keep the code to ensure we're executing on the UI thread (for UI code) for debug builds
// but this can be overridden to either (a) remove it from debug builds or (b) keep it in release builds
EnsureUIThread = thread_check.HasValue ? thread_check.Value : App.EnableDebug,
+ MarshalNativeExceptionsState = !App.RequiresPInvokeWrappers ? null : new PInvokeWrapperGenerator ()
+ {
+ SourcePath = Path.Combine (Cache.Location, "pinvokes.m"),
+ HeaderPath = Path.Combine (Cache.Location, "pinvokes.h"),
+ Registrar = (StaticRegistrar) BuildTarget.StaticRegistrar,
+ },
};
+ linker_options = options;
+
Mono.Linker.LinkContext context;
MonoMac.Tuner.Linker.Process (options, out context, out resolved_assemblies);
BuildTarget.LinkContext = (context as MonoMacLinkContext);
diff --git a/tools/mmp/error.cs b/tools/mmp/error.cs
index e8688b58c095..6211489ba6d5 100644
--- a/tools/mmp/error.cs
+++ b/tools/mmp/error.cs
@@ -33,6 +33,8 @@ namespace Xamarin.Bundler {
// MM0068 Invalid value for target framework: {0}.
// MM0079 Internal Error - No executable was copied into the app bundle. Please contact 'support@xamarin.com'
// Warning MT0080 Disabling NewRefCount, --new-refcount:false, is deprecated.
+ // MM0088 ** Reserved mtouch **
+ // MM0089 ** Reserved mtouch **
// MM1xxx file copy / symlinks (project related)
// MM14xx Product assemblies
// MM1401 The required '{0}' assembly is missing from the references
diff --git a/tools/mmp/mmp.csproj b/tools/mmp/mmp.csproj
index 600c5c7324c4..6cbce1125e19 100644
--- a/tools/mmp/mmp.csproj
+++ b/tools/mmp/mmp.csproj
@@ -263,6 +263,9 @@
external\ErrorHelper.cs
+
+ external\ExceptionMode.cs
+
external\Registrar.cs
@@ -314,6 +317,9 @@
external\PlatformAvailability2.cs
+
+ external\PInvokeWrapperGenerator.cs
+
diff --git a/tools/mtouch/.gitignore b/tools/mtouch/.gitignore
index fe01bb419d11..2673ba7899f7 100644
--- a/tools/mtouch/.gitignore
+++ b/tools/mtouch/.gitignore
@@ -3,6 +3,7 @@ SdkVersions.cs
*.dylib
*.dSYM
*.registrar.*.m
+*.registrar.*.h
simlauncher-sgen
simlauncher32-sgen
simlauncher64-sgen
diff --git a/tools/mtouch/Application.cs b/tools/mtouch/Application.cs
index 2b8783494636..71e8ae48a81b 100644
--- a/tools/mtouch/Application.cs
+++ b/tools/mtouch/Application.cs
@@ -7,6 +7,7 @@
using System.IO;
using System.Text;
using System.Threading;
+using System.Threading.Tasks;
using MonoTouch.Tuner;
@@ -539,8 +540,11 @@ public void RunRegistrar ()
}
break;
}
- var registrar = XamCore.Registrar.StaticRegistrar.Generate (this, resolvedAssemblies, simulator: true, single_assembly: Path.GetFileNameWithoutExtension (RootAssembly));
- Driver.WriteIfDifferent (registrar_m, registrar);
+
+ BuildTarget = BuildTarget.Simulator;
+
+ var registrar = new XamCore.Registrar.StaticRegistrar (this);
+ registrar.GenerateSingleAssembly (resolvedAssemblies, Path.ChangeExtension (registrar_m, "h"), registrar_m, Path.GetFileNameWithoutExtension (RootAssembly));
}
public void Build ()
@@ -847,6 +851,8 @@ void Initialize ()
Namespaces.Initialize ();
+ InitializeCommon ();
+
Driver.Watch ("Resolve References", 1);
}
@@ -876,6 +882,9 @@ void SelectRegistrar ()
Registrar = RegistrarMode.LegacyDynamic;
}
}
+
+ foreach (var target in Targets)
+ target.SelectStaticRegistrar ();
}
// Select all abi from the list matching the specified mask.
@@ -1537,6 +1546,24 @@ public static void ProcessFrameworkForArguments (StringBuilder args, string fw,
}
}
+ public class BuildTasks : List
+ {
+ static void Execute (BuildTask v)
+ {
+ var next = v.Execute ();
+ if (next != null)
+ Parallel.ForEach (next, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, Execute);
+ }
+
+ public void ExecuteInParallel ()
+ {
+ if (Count == 0)
+ return;
+
+ Parallel.ForEach (this, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, Execute);
+ }
+ }
+
public abstract class BuildTask
{
public IEnumerable NextTasks;
@@ -1670,10 +1697,16 @@ protected override void Build ()
}
internal class RegistrarTask : CompileTask {
+ public static void Create (List tasks, IEnumerable abis, Target target, string ifile)
+ {
+ foreach (var abi in abis)
+ Create (tasks, abi, target, ifile);
+ }
+
public static void Create (List tasks, Abi abi, Target target, string ifile)
{
var arch = abi.AsArchString ();
- var ofile = Path.Combine (Cache.Location, "registrar." + arch + ".o");
+ var ofile = Path.Combine (Cache.Location, Path.GetFileNameWithoutExtension (ifile) + "." + arch + ".o");
if (!Application.IsUptodate (ifile, ofile)) {
tasks.Add (new RegistrarTask ()
diff --git a/tools/mtouch/Assembly.cs b/tools/mtouch/Assembly.cs
index 0483dd523481..fec9b1f614cc 100644
--- a/tools/mtouch/Assembly.cs
+++ b/tools/mtouch/Assembly.cs
@@ -153,7 +153,7 @@ public bool CopyToDirectory (string directory, bool reload = true, bool check_ca
IEnumerable CreateCompileTasks (string s, string asm_infile, string llvm_infile, Abi abi)
{
- var compile_tasks = new List ();
+ var compile_tasks = new BuildTasks ();
if (asm_infile != null) {
var task = CreateCompileTask (s, asm_infile, abi);
if (task != null)
@@ -372,7 +372,7 @@ public bool Symlink ()
return symlink_failed;
}
- public void CreateCompilationTasks (List tasks, string build_dir, IEnumerable abis)
+ public void CreateCompilationTasks (BuildTasks tasks, string build_dir, IEnumerable abis)
{
var assembly = Path.Combine (build_dir, FileName);
diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile
index ff063f80c3cc..f1ae91aabf19 100644
--- a/tools/mtouch/Makefile
+++ b/tools/mtouch/Makefile
@@ -91,6 +91,7 @@ COMMON_SOURCES = \
$(TOP)/src/build/ios/Constants.cs \
error.cs \
$(TOP)/src/ObjCRuntime/ErrorHelper.cs \
+ $(TOP)/src/ObjCRuntime/ExceptionMode.cs \
$(MONO_DIR)/mcs/class/Mono.Options/Mono.Options/Options.cs
MTOUCH_SOURCES = \
@@ -109,6 +110,7 @@ MTOUCH_SOURCES = \
$(TOP)/tools/common/MachO.cs \
mtouch.cs \
OldStaticRegistrar.cs \
+ $(TOP)/tools/common/PInvokeWrapperGenerator.cs \
$(TOP)/tools/common/TargetFramework.cs \
$(TOP)/tools/common/StaticRegistrar.cs \
$(TOP)/src/ObjCRuntime/Registrar.core.cs \
@@ -149,8 +151,11 @@ monotouch-fixes.x86_64.dylib: monotouch-fixes.c Makefile
monotouch-fixes.dylib: monotouch-fixes.i386.dylib monotouch-fixes.x86_64.dylib
$(Q_LIPO) lipo -create -output $@ $^
-# keep in sync with mtouch.cs (but keep current differences too)
UNREFERENCED_SYMBOLS = \
+ _xamarin_dyn_objc_msgSend \
+ _xamarin_dyn_objc_msgSendSuper \
+ _xamarin_dyn_objc_msgSend_stret \
+ _xamarin_dyn_objc_msgSendSuper_stret \
_xamarin_init_nsthread \
_xamarin_get_block_descriptor \
_xamarin_get_locale_country_code \
@@ -314,32 +319,32 @@ bin/Makefile/Mono.Cecil.dll: $(MONO_CECIL_DLL) | bin/Makefile
bin/Makefile/Mono.Cecil.Mdb.dll: $(MONO_CECIL_MDB_DLL) | bin/Makefile
$(Q) cp $<* $(dir $@)
-%.registrar.ios.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.iOS/%.dll $(LOCAL_MTOUCH)
+%.registrar.ios.m %.registrar.ios.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.iOS/%.dll $(LOCAL_MTOUCH)
$(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(IOS_SDK_VERSION) $< --registrar:static --target-framework Xamarin.iOS,v1.0 --abi i386,x86_64
- $(Q) touch $@
+ $(Q) touch $*.registrar.ios.m $*.registrar.ios.h
-%.registrar.watchos.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.WatchOS/%.dll $(LOCAL_MTOUCH)
+%.registrar.watchos.m %.registrar.watchos.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.WatchOS/%.dll $(LOCAL_MTOUCH)
$(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(WATCH_SDK_VERSION) $< --registrar:static --target-framework Xamarin.WatchOS,1.0 --abi i386
- $(Q) touch $@
+ $(Q) touch $*.registrar.watchos.m $*.registrar.watchos.h
-%.registrar.tvos.m: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.TVOS/%.dll $(LOCAL_MTOUCH)
+%.registrar.tvos.m %.registrar.tvos.h: $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/mono/Xamarin.TVOS/%.dll $(LOCAL_MTOUCH)
$(Q_GEN) $(SYSTEM_MONO) --debug bin/Makefile/mtouch.exe --xamarin-framework-directory=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX) -q --runregistrar:$(abspath $@) --sdkroot $(XCODE_DEVELOPER_ROOT) --sdk $(TVOS_SDK_VERSION) $< --registrar:static --target-framework Xamarin.TVOS,1.0 --abi x86_64
- $(Q) touch $@
+ $(Q) touch $*.registrar.tvos.m $*.registrar.tvos.h
%.registrar.ios.a: %.registrar.ios.i386.a %.registrar.ios.x86_64.a
$(Q_LIPO) $(DEVICE_BIN_PATH)/lipo -create -output $@ $^
-%.registrar.ios.i386.a: %.registrar.ios.m
- $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR86_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include
+%.registrar.ios.i386.a: %.registrar.ios.m %.registrar.ios.h
+ $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR86_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include
-%.registrar.ios.x86_64.a: %.registrar.ios.m
- $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR64_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include
+%.registrar.ios.x86_64.a: %.registrar.ios.m %.registrar.ios.h
+ $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATOR64_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/SDKs/MonoTouch.iphonesimulator.sdk/usr/include
-%.registrar.watchos.a: %.registrar.watchos.m
- $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORWATCH_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(XAMARIN_WATCHSIMULATOR_SDK)/usr/include
+%.registrar.watchos.a: %.registrar.watchos.m %.registrar.watchos.h
+ $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORWATCH_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(XAMARIN_WATCHSIMULATOR_SDK)/usr/include
-%.registrar.tvos.a: %.registrar.tvos.m
- $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORTV_CFLAGS) -x objective-c++ -o $@ -c $^ -Wall -I$(IOS_DESTDIR)$(XAMARIN_TVSIMULATOR_SDK)/usr/include
+%.registrar.tvos.a: %.registrar.tvos.m %.registrar.tvos.h
+ $(Q_CC) $(IOS_CC) -DDEBUG -g -gdwarf-2 $(SIMULATORTV_CFLAGS) -x objective-c++ -o $@ -c $< -Wall -I$(IOS_DESTDIR)$(XAMARIN_TVSIMULATOR_SDK)/usr/include
TARGETS = \
$(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/bin/mtouch \
@@ -422,6 +427,6 @@ include $(TOP)/mk/rules.mk
# make will automatically consider files created in chained implicit rules as temporary files, and delete them afterwards
# marking those files as .SECONDARY will prevent that deletion.
-.SECONDARY: $(foreach file,Xamarin.iOS MonoTouch.Dialog-1 MonoTouch.NUnitLite,$(file).registrar.ios.a $(file).registrar.ios.i386.a $(file).registrar.ios.x86_64.a $(file).registrar.ios.m)
-.SECONDARY: $(foreach file,Xamarin.WatchOS,$(file).registrar.watchos.a $(file).registrar.watchos.m)
-.SECONDARY: $(foreach file,Xamarin.TVOS MonoTouch.Dialog-1,$(file).registrar.tvos.a $(file).registrar.tvos.m)
+.SECONDARY: $(foreach file,Xamarin.iOS MonoTouch.Dialog-1 MonoTouch.NUnitLite,$(file).registrar.ios.a $(file).registrar.ios.i386.a $(file).registrar.ios.x86_64.a $(file).registrar.ios.m $(file).registrar.ios.h)
+.SECONDARY: $(foreach file,Xamarin.WatchOS,$(file).registrar.watchos.a $(file).registrar.watchos.m $(file).registrar.watchos.h)
+.SECONDARY: $(foreach file,Xamarin.TVOS MonoTouch.Dialog-1,$(file).registrar.tvos.a $(file).registrar.tvos.m $(file).registrar.tvos.h)
diff --git a/tools/mtouch/OldStaticRegistrar.cs b/tools/mtouch/OldStaticRegistrar.cs
index 6987317df3fe..e4bb4cb965d1 100644
--- a/tools/mtouch/OldStaticRegistrar.cs
+++ b/tools/mtouch/OldStaticRegistrar.cs
@@ -17,6 +17,7 @@
using XamCore.Registrar;
using XamCore.ObjCRuntime;
using Xamarin.Linker;
+using Mono.Linker;
namespace XamCore.Registrar {
@@ -777,7 +778,7 @@ public static IEnumerable Describe (IEnumerable asse
}
}
- class OldStaticRegistrar {
+ class OldStaticRegistrar : IStaticRegistrar {
static StringBuilder headers = new StringBuilder ();
static StringBuilder structs = new StringBuilder ();
static bool verbose = false;
@@ -879,7 +880,12 @@ static string GetPrintfFormatSpecifier (TypeDefinition type, out bool unknown)
static HashSet trampoline_names = new HashSet ();
static HashSet namespaces = new HashSet ();
static HashSet structures = new HashSet ();
-
+
+ public LinkContext LinkContext {
+ get { throw new NotImplementedException (); }
+ set { throw new NotImplementedException (); }
+ }
+
static void CheckNamespace (string ns)
{
if (!ns.StartsWith ("MonoTouch."))
@@ -1659,6 +1665,22 @@ static void Specialize (StringBuilder sb, Method method)
method.SpecializedTrampoline = name;
}
+ public void GeneratePInvokeWrappers (List marshal_exception_pinvokes, string header_path, string source_path)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public void GenerateSingleAssembly (IEnumerable assemblies, string header_path, string source_path, string single_assembly)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public void Generate (IEnumerable assemblies, string header_path, string source_path)
+ {
+ Driver.WriteIfDifferent (header_path, string.Empty);
+ Driver.WriteIfDifferent (source_path, Generate (assemblies));
+ }
+
public static string Generate (IEnumerable assemblies)
{
var classes = TypeSystemDescriptor.Describe (assemblies);
diff --git a/tools/mtouch/Target.cs b/tools/mtouch/Target.cs
index 45cc9a8197bd..235cfde02f3c 100644
--- a/tools/mtouch/Target.cs
+++ b/tools/mtouch/Target.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;
+using System.Xml;
using MonoTouch.Tuner;
@@ -16,6 +17,8 @@
using Xamarin.Utils;
+using XamCore.Registrar;
+
namespace Xamarin.Bundler
{
public partial class Target {
@@ -46,6 +49,8 @@ public partial class Target {
// If any assemblies were updated (only set to false if the linker is disabled and no assemblies were modified).
bool any_assembly_updated = true;
+ BuildTasks compile_tasks = new BuildTasks ();
+
// If we didn't link the final executable because the existing binary is up-to-date.
public bool cached_executable;
@@ -113,7 +118,7 @@ public void LoadSymbols ()
IEnumerable GetAssemblies ()
{
if (App.LinkMode == LinkMode.None)
- return Resolver.GetAssemblies ();
+ return ManifestResolver.GetAssemblies ();
List assemblies = new List ();
if (LinkContext == null) {
@@ -159,19 +164,29 @@ public IEnumerable GetRequiredSymbols ()
foreach (var ep in File.ReadAllLines (cache_location))
entry_points.Add (ep, null);
} else {
+ List marshal_exception_pinvokes;
if (LinkContext == null) {
// This happens when using the simlauncher and the msbuild tasks asked for a list
// of symbols (--symbollist). In that case just produce an empty list, since the
// binary shouldn't end up stripped anyway.
entry_points = new Dictionary ();
+ marshal_exception_pinvokes = new List ();
} else {
entry_points = LinkContext.RequiredSymbols;
+ marshal_exception_pinvokes = LinkContext.MarshalExceptionPInvokes;
}
-
+
// keep the debugging helper in debugging binaries only
if (App.EnableDebug && !App.EnableBitCode)
entry_points.Add ("mono_pmip", null);
+ if (App.IsSimulatorBuild) {
+ entry_points.Add ("xamarin_dyn_objc_msgSend", null);
+ entry_points.Add ("xamarin_dyn_objc_msgSendSuper", null);
+ entry_points.Add ("xamarin_dyn_objc_msgSend_stret", null);
+ entry_points.Add ("xamarin_dyn_objc_msgSendSuper_stret", null);
+ }
+
File.WriteAllText (cache_location, string.Join ("\n", entry_points.Keys.ToArray ()));
}
return entry_points.Keys;
@@ -352,7 +367,7 @@ public void LinkAssemblies (string main, ref List assemblies, string out
resolver.AddSearchDirectory (Resolver.RootDirectory);
resolver.AddSearchDirectory (Resolver.FrameworkDirectory);
- var options = new LinkerOptions {
+ LinkerOptions = new LinkerOptions {
MainAssembly = Resolver.Load (main),
OutputDirectory = output_dir,
LinkMode = App.LinkMode,
@@ -372,10 +387,16 @@ public void LinkAssemblies (string main, ref List assemblies, string out
IsDualBuild = App.IsDualBuild,
Unified = App.IsUnified,
DumpDependencies = App.LinkerDumpDependencies,
- RuntimeOptions = App.RuntimeOptions
+ RuntimeOptions = App.RuntimeOptions,
+ MarshalNativeExceptionsState = !App.RequiresPInvokeWrappers ? null : new PInvokeWrapperGenerator ()
+ {
+ SourcePath = Path.Combine (ArchDirectory, "pinvokes.m"),
+ HeaderPath = Path.Combine (ArchDirectory, "pinvokes.h"),
+ Registrar = (StaticRegistrar) StaticRegistrar,
+ },
};
- MonoTouch.Tuner.Linker.Process (options, out link_context, out assemblies);
+ MonoTouch.Tuner.Linker.Process (LinkerOptions, out link_context, out assemblies);
// reset resolver
foreach (var file in assemblies) {
@@ -516,6 +537,8 @@ public void ProcessAssemblies ()
// * Linking
// Copy assemblies to LinkDirectory
// Link and save to PreBuildDirectory
+ // If marshalling native exceptions:
+ // * Generate/calculate P/Invoke wrappers and save to PreBuildDirectory
// * Has resourced to be removed:
// Remove resource and save to NoResDirectory
// Copy to BuildDirectory. [Why not save directly to BuildDirectory? Because otherwise if we're rebuilding
@@ -528,7 +551,10 @@ public void ProcessAssemblies ()
// Strip managed code save to TargetDirectory (or just copy the file if stripping is disabled).
//
// * No linking
- // Copy assembly to PreBuildDirectory.
+ // If marshalling native exceptions:
+ // Generate/calculate P/Invoke wrappers and save to PreBuildDirectory.
+ // If not marshalling native exceptions:
+ // Copy assemblies to PreBuildDirectory
// * Has resourced to be removed:
// Remove resource and save to NoResDirectory
// Copy to BuildDirectory.
@@ -588,6 +614,13 @@ public void ProcessAssemblies ()
ManagedLink ();
+ if (App.RequiresPInvokeWrappers) {
+ // Write P/Invokes
+ var state = LinkerOptions.MarshalNativeExceptionsState;
+ state.End ();
+ RegistrarTask.Create (compile_tasks, Abis, this, state.SourcePath);
+ }
+
// Now the assemblies are in PreBuildDirectory.
//
@@ -621,22 +654,40 @@ public void ProcessAssemblies ()
Frameworks.ExceptWith (WeakFrameworks);
}
- public void Compile ()
+ public void SelectStaticRegistrar ()
{
- var tasks = new List ();
+ switch (App.Registrar) {
+ case RegistrarMode.LegacyStatic:
+ case RegistrarMode.Legacy:
+ case RegistrarMode.LegacyDynamic:
+ StaticRegistrar = new OldStaticRegistrar ();
+ break;
+ case RegistrarMode.Static:
+ case RegistrarMode.Dynamic:
+ case RegistrarMode.Default:
+ StaticRegistrar = new StaticRegistrar (this)
+ {
+ LinkContext = LinkContext,
+ };
+ break;
+ }
+ }
+ public void Compile ()
+ {
// Compile the managed assemblies into object files or shared libraries
if (App.IsDeviceBuild) {
foreach (var a in Assemblies)
- a.CreateCompilationTasks (tasks, BuildDirectory, Abis);
+ a.CreateCompilationTasks (compile_tasks, BuildDirectory, Abis);
}
// The static registrar.
List registration_methods = null;
if (App.Registrar == RegistrarMode.Static || App.Registrar == RegistrarMode.LegacyStatic) {
var registrar_m = Path.Combine (ArchDirectory, "registrar.m");
- if (!Application.IsUptodate (Assemblies.Select (v => v.FullPath), new string[] { registrar_m })) {
- registrar_m = Driver.RunRegistrar (this, Assemblies, BuildDirectory, BuildDirectory, App.Registrar == RegistrarMode.LegacyStatic, Is64Build, registrar_m);
+ var registrar_h = Path.Combine (ArchDirectory, "registrar.h");
+ if (!Application.IsUptodate (Assemblies.Select (v => v.FullPath), new string[] { registrar_m, registrar_h })) {
+ StaticRegistrar.Generate (Assemblies.Select ((a) => a.AssemblyDefinition), registrar_h, registrar_m);
registration_methods = new List ();
registration_methods.Add ("xamarin_create_classes");
Driver.Watch ("Registrar", 1);
@@ -644,8 +695,7 @@ public void Compile ()
Driver.Log (3, "Target '{0}' is up-to-date.", registrar_m);
}
- foreach (var abi in Abis)
- RegistrarTask.Create (tasks, abi, this, registrar_m);
+ RegistrarTask.Create (compile_tasks, Abis, this, registrar_m);
}
if (App.Registrar == RegistrarMode.Dynamic && App.IsSimulatorBuild && App.LinkMode == LinkMode.None && App.IsUnified) {
@@ -698,19 +748,10 @@ public void Compile ()
// The main method.
foreach (var abi in Abis)
- MainTask.Create (tasks, this, abi, Assemblies, App.AssemblyName, registration_methods);
+ MainTask.Create (compile_tasks, this, abi, Assemblies, App.AssemblyName, registration_methods);
// Start compiling.
- if (tasks.Count > 0) {
- Action action = null;
- action = (v) => {
- var next = v.Execute ();
- if (next != null)
- Parallel.ForEach (next, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, action);
- };
-
- Parallel.ForEach (tasks, new ParallelOptions () { MaxDegreeOfParallelism = Environment.ProcessorCount }, action);
- }
+ compile_tasks.ExecuteInParallel ();
if (App.FastDev) {
foreach (var a in Assemblies) {
diff --git a/tools/mtouch/Tuning.cs b/tools/mtouch/Tuning.cs
index 745aadfdb303..e1004cc4ada3 100644
--- a/tools/mtouch/Tuning.cs
+++ b/tools/mtouch/Tuning.cs
@@ -33,6 +33,7 @@ public class LinkerOptions {
public bool IsDualBuild { get; set; }
public bool Unified { get; set; }
public bool DumpDependencies { get; set; }
+ internal PInvokeWrapperGenerator MarshalNativeExceptionsState { get; set; }
internal RuntimeOptions RuntimeOptions { get; set; }
public MonoTouchLinkContext LinkContext { get; set; }
@@ -183,7 +184,7 @@ static Pipeline CreatePipeline (LinkerOptions options)
pipeline.AppendStep (new FixModuleFlags ());
}
- pipeline.AppendStep (new ListExportedSymbols ());
+ pipeline.AppendStep (new ListExportedSymbols (options.MarshalNativeExceptionsState));
pipeline.AppendStep (new OutputStep ());
@@ -228,6 +229,7 @@ static ResolveFromXmlStep GetResolveStep (string filename)
public class MonoTouchLinkContext : LinkContext {
Dictionary required_symbols;
+ List marshal_exception_pinvokes;
public Dictionary RequiredSymbols {
get {
@@ -237,6 +239,14 @@ public Dictionary RequiredSymbols {
}
}
+ public List MarshalExceptionPInvokes {
+ get {
+ if (marshal_exception_pinvokes == null)
+ marshal_exception_pinvokes = new List ();
+ return marshal_exception_pinvokes;
+ }
+ }
+
public MonoTouchLinkContext (Pipeline pipeline, AssemblyResolver resolver)
: base (pipeline, resolver)
{
diff --git a/tools/mtouch/error.cs b/tools/mtouch/error.cs
index e09c306b68fd..756f22f21794 100644
--- a/tools/mtouch/error.cs
+++ b/tools/mtouch/error.cs
@@ -95,6 +95,8 @@ namespace Xamarin.Bundler {
// MT0085 No reference to '{0}' was found. It will be added automatically.
// MT0086 A target framework (--target-framework) must be specified when building for TVOS or WatchOS.
// Warning MT0087
+ // MT0088 The GC must be in cooperative mode for watchOS apps. Please remove the --coop:false argument to mtouch.
+ // MT0089 The option '{0}' cannot take the value '{1}' when cooperative mode is enabled for the GC.
// MT0091 This version of Xamarin.iOS requires the {0} {1} SDK (shipped with Xcode {2}) when the managed linker is disabled. Either upgrade Xcode, or enable the managed linker.
// MT0092 The option '{0}' is required.
// MT0093 Could not find 'mlaunch'.
diff --git a/tools/mtouch/mtouch.cs b/tools/mtouch/mtouch.cs
index 00477a7ce691..7d479eedd6fc 100644
--- a/tools/mtouch/mtouch.cs
+++ b/tools/mtouch/mtouch.cs
@@ -63,6 +63,7 @@
using MonoTouch.Tuner;
using XamCore.Registrar;
+using XamCore.ObjCRuntime;
using Xamarin.Linker;
using Xamarin.Utils;
@@ -651,6 +652,8 @@ public static string GenerateMain (IEnumerable assemblies, string asse
sw.WriteLine ("\tmono_use_llvm = {0};", enable_llvm ? "TRUE" : "FALSE");
sw.WriteLine ("\txamarin_log_level = {0};", verbose);
sw.WriteLine ("\txamarin_arch_name = \"{0}\";", abi.AsArchString ());
+ sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", app.MarshalManagedExceptions);
+ sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", app.MarshalObjectiveCExceptions);
if (app.EnableDebug)
sw.WriteLine ("\txamarin_debug_mode = TRUE;");
if (!string.IsNullOrEmpty (app.MonoGCParams))
@@ -907,6 +910,15 @@ public static bool CanWeSymlinkTheApplication ()
if (app.Registrar == RegistrarMode.Static || app.Registrar == RegistrarMode.LegacyStatic || app.Registrar == RegistrarMode.LegacyDynamic)
return false;
+ // The default exception marshalling differs between release and debug mode, but we
+ // only have one simlauncher, so to use the simlauncher we'd have to chose either
+ // debug or release mode. Debug is more frequent, so make that the fast path.
+ if (!app.EnableDebug)
+ return false;
+
+ if (app.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.UnwindManagedCode)
+ return false; // UnwindManagedCode is the default for debug builds.
+
return true;
}
@@ -1350,6 +1362,8 @@ static int Main2 (string [] args)
{ "xamarin-framework-directory=", "The framework directory", v => { mtouch_dir = v; }, true },
};
+ AddSharedOptions (os);
+
try {
assemblies = os.Parse (args);
}
@@ -1718,25 +1732,6 @@ static bool IsBoundAssembly (Assembly s)
return false;
}
- public static string RunRegistrar (Target target, List assemblies, string assemblies_path, string output_dir, bool old, bool is_64_bits, string out_file = null)
- {
- const string registrar_file = "registrar.m";
-
- var resolvedAssemblies = assemblies.Select (asm => asm.AssemblyDefinition);
- var output_file = out_file ?? Path.Combine (output_dir, registrar_file);
- var code = string.Empty;
-
- if (old) {
- code = OldStaticRegistrar.Generate (resolvedAssemblies);
- } else {
- code = StaticRegistrar.Generate (app, resolvedAssemblies, target.App.IsSimulatorBuild, is_64_bits, target.LinkContext);
- }
-
- WriteIfDifferent (output_file, code);
-
- return output_file;
- }
-
struct timespec {
public IntPtr tv_sec;
public IntPtr tv_nsec;
diff --git a/tools/mtouch/mtouch.csproj b/tools/mtouch/mtouch.csproj
index 7b5e9644cfc8..1e2778a1294e 100644
--- a/tools/mtouch/mtouch.csproj
+++ b/tools/mtouch/mtouch.csproj
@@ -317,6 +317,12 @@
external\ErrorHelper.cs
+
+ external\ExceptionMode.cs
+
+
+ common\PInvokeWrapperGenerator.cs
+
diff --git a/tools/mtouch/simlauncher.m b/tools/mtouch/simlauncher.m
index 79aa558c35e5..406ae9c58a9a 100644
--- a/tools/mtouch/simlauncher.m
+++ b/tools/mtouch/simlauncher.m
@@ -21,6 +21,12 @@ void xamarin_setup_impl ()
xamarin_create_classes_Xamarin_iOS ();
#else
xamarin_use_new_assemblies = FALSE;
+#endif
+ xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDisable;
+#if DEBUG
+ xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeUnwindManagedCode;
+#else
+ xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDisabled;
#endif
}