Bug 4781 - cannot make trampoline for delegate-typed parameter (passing a block to a block)
Summary: cannot make trampoline for delegate-typed parameter (passing a block to a block)
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 5.2
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Rolf Bjarne Kvinge [MSFT]
: 12927 ()
Depends on: 4718
  Show dependency tree
Reported: 2012-04-30 18:30 UTC by Daniel
Modified: 2013-09-26 08:34 UTC (History)
8 users (show)

Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Daniel 2012-04-30 18:30:01 UTC
Attempting to compile this code in binding a library (the TouchDB library from couchbase)

delegate void TDMapEmitBlock(NSObject key, NSObject value);
delegate void TDMapBlock(NSDictionary doc, TDMapEmitBlock emit); work..
delegate void TDReduceBlock(NSArray keys, NSArray values, bool rereduce);
[BaseType (typeof (NSObject))]
interface TDView 
  [Export ("setMapBlock:reduceBlock:version:")]
  bool SetMapReduce (TDMapBlock mapBlock, TDReduceBlock reduceBlock, string version);

Yields this:

/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Xamarin.ObjcBinding.CSharp.targets: Error: Tool exited with code: 1. Output: MakeTrampoline: do not know how to make a trampoline for TDMapEmitBlock emit

Unhandled Exception: System.Exception: Exception of type 'System.Exception' was thrown.
  at Generator.MakeTrampoline (System.Type t) [0x00000] in <filename unknown>:0 
  at Generator.GenerateMethodBody (System.Type type, System.Reflection.MethodInfo mi, Boolean virtual_method, Boolean is_static, System.String sel, Boolean null_allowed_override, System.String var_name, BodyOption body_options) [0x00000] in <filename unknown>:0 
  at Generator.GenerateMethod (System.Type type, System.Reflection.MethodInfo mi, Boolean is_model) [0x00000] in <filename unknown>:0 
  at Generator.Generate (System.Type type) [0x00000] in <filename unknown>:0 
  at Generator.Go () [0x00000] in <filename unknown>:0 
  at BindingTouch.Main (System.String[] args) [0x00000] in <filename unknown>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.Exception: Exception of type 'System.Exception' was thrown.
  at Generator.MakeTrampoline (System.Type t) [0x00000] in <filename unknown>:0 
  at Generator.GenerateMethodBody (System.Type type, System.Reflection.MethodInfo mi, Boolean virtual_method, Boolean is_static, System.String sel, Boolean null_allowed_override, System.String var_name, BodyOption body_options) [0x00000] in <filename unknown>:0 
  at Generator.GenerateMethod (System.Type type, System.Reflection.MethodInfo mi, Boolean is_model) [0x00000] in <filename unknown>:0 
  at Generator.Generate (System.Type type) [0x00000] in <filename unknown>:0 
  at Generator.Go () [0x00000] in <filename unknown>:0 
  at BindingTouch.Main (System.String[] args) [0x00000] in <filename unknown>:0 

Basically, I need to invoke a Block passed back in as an argument to my delegate and it doesnt look like that case was considered. For now I might be able to modify the source code of the library but obviously thats not a long term solution.
Comment 1 Rolf Bjarne Kvinge [MSFT] 2012-05-10 19:12:25 UTC
For this to work we'd first need to make Marshal.GetDelegateForFunctionPointer work, which has already been filed as #4718.
Comment 2 Daniel 2012-06-05 20:17:53 UTC
Any chance there is a user-side workaround now that 4718 is resolved?
Comment 3 Miguel de Icaza [MSFT] 2012-07-12 16:04:33 UTC
Hello Daniel,

A user provided version could be done by adding some native code to your library (I assume this is TouchDB?) that does something like this, put this on a Objective-C class called "MyHelpers":

+(void)callMapBlock:(TDMapBlock) block with:(id) key and:(id) value
     block (key, value);

Then you would need to bind that method like this:

[Export ("MyHelpers")]
class MyHelpers {
    [Static, Export ("callMapBlock:with:and:")]
   void CallMapBlock (IntPtr block, NSObject key, NSObject value);

Then you change your delegate to not include a delegate and instead use an IntPtr:

delegate void TDMapBlock (NSDictionary dict, IntPtr block);

[Export ("defineViewNamed:mapBlock:")]
void DefineView (string name, TDMapBlock block);

Then this is how you would call DefineView:

foo.DefineView ("hello", (NSDictionary dict, IntPtr block) => {
   // Use the helper function here to call
   MyHelpers.CallMapBlock (block, ARG1, ARG2);
Comment 4 Miguel de Icaza [MSFT] 2012-07-12 18:14:37 UTC
We have a prototype.

Can I have a test case to try it out?
Comment 5 Jose Samonte 2012-07-25 01:55:04 UTC
Good day,

I tried the suggested workaround, but I immediately get a SIGSEGV error calling the foo.DefineView()
Comment 6 Jose Samonte 2012-07-25 15:29:42 UTC
Sorry, I think it was just a version problem with the TouchDB and CouchCocoa libraries. Thanks.
Comment 7 Jose Samonte 2012-07-26 06:25:06 UTC
Good day,

I was able to compile the workaround, and register the Objective-C block to TouchDB, but it seems that the call to 'block (key, value)' doesn't work or gets called by TouchDB with this indirection.
Comment 8 Miguel de Icaza [MSFT] 2012-07-26 12:03:38 UTC
Jose, have you seen the partial implementation that we have for block support, and the prototype bindings thatw e have in monotouch-bindings?
Comment 9 Jose Samonte 2012-07-26 16:18:02 UTC
Sorry sir, I didn't understand your question. Are you referring to a prototype you have for the beta for this fix?
Comment 10 Jose Samonte 2012-07-26 16:26:29 UTC
Thanks sir Miguel. I found it. Will try to test it. I really appreciate your help.
Comment 11 Jose Samonte 2012-07-26 18:15:38 UTC
Good day,

I tried the 5.3.5 version following the sample code, but I'm getting some errors when the code blocks to do the queries(emit is called) are being executed (I apologize for the long stack trace).

For a filtered query such as:

designDoc.DefineView("by-username", (doc, emit) =>
 var username = doc["UserName"];
 if (username != null)
   emit(username, doc);
} , "1.0");

I get 'Type MonoTouch.Foundation.NSObject which is passed to unmanaged code must have a StructLayout attribute.'.

For a query such as:

designDoc.DefineView("_all", (doc, emit) =>
 var id = doc["_id"];
 if (id.ToString().IndexOf("appuser-") == 0)
   emit(null, null);
} , "1.0");

I get:
at Couchbase.CouchDesignDocument.TrampolineTDMap (intptr,intptr,intptr) <IL 0x00074, 0x0025f>

  at (wrapper native-to-managed) Couchbase.CouchDesignDocument.TrampolineTDMap (intptr,intptr,intptr) <IL 0x0001f, 0xffffffff>

Native stacktrace:

	 0   TrigramMobile                       0x000f672c mono_handle_native_sigsegv + 284

	 1   TrigramMobile                       0x0006b848 mono_sigsegv_signal_handler + 248

	 2   libsystem_c.dylib                   0x9450659b _sigtramp + 43

	 3   ???                                 0xffffffff 0x0 + 4294967295

	 4   ???                                 0x104ea78e 0x0 + 273590158

	 5   ???                                 0x104ea028 0x0 + 273588264

	 6   ???                                 0x0ee9ac78 0x0 + 250195064

	 7   TrigramMobile                       0x00032f1d -[TDView updateIndex] + 3041

	 8   TrigramMobile                       0x00058603 -[TDRouter(Handlers) queryDesignDoc:view:keys:] + 550

	 9   TrigramMobile                       0x00058800 -[TDRouter(Handlers) do_GET:designDocID:view:] + 55

	 10  TrigramMobile                       0x000389fb -[TDRouter route] + 2635

	 11  TrigramMobile                       0x00038a8d -[TDRouter run] + 52

	 12  TrigramMobile                       0x000390c1 __17-[TDRouter start]_block_invoke_0 + 71

	 13  TrigramMobile                       0x00036990 __32-[TDServer tellDatabaseManager:]_block_invoke_0 + 40

	 14  CoreFoundation                      0x019efe42 -[NSObject performSelector:withObject:] + 66

	 15  Foundation                          0x006c89df __NSThreadPerformPerform + 254

	 16  CoreFoundation                      0x019c294f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15

	 17  CoreFoundation                      0x01925b43 __CFRunLoopDoSources0 + 243

	 18  CoreFoundation                      0x01925424 __CFRunLoopRun + 1012

	 19  CoreFoundation                      0x01924d84 CFRunLoopRunSpecific + 212

	 20  CoreFoundation                      0x01924c9b CFRunLoopRunInMode + 123

	 21  Foundation                          0x006e840f -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 300

	 22  TrigramMobile                       0x000366d1 -[TDServer runServerThread] + 417

	 23  Foundation                          0x006b44d6 -[NSThread main] + 76

	 24  Foundation                          0x006b4447 __NSThread__main__ + 1258

	 25  libsystem_c.dylib                   0x944aeed9 _pthread_start + 335

	 26  libsystem_c.dylib                   0x944b26de thread_start + 34
Comment 12 Miguel de Icaza [MSFT] 2012-07-27 08:40:48 UTC

We have a binding for that library that does not require you to do any of the workarounds suggested.

Comment 13 Jose Samonte 2012-07-27 16:45:15 UTC
Yes sir, I already used this binding, from which I got the errors I posted previously.
Comment 14 Sebastien Pouliot 2013-07-04 10:03:34 UTC
*** Bug 12927 has been marked as a duplicate of this bug. ***
Comment 15 Sebastien Pouliot 2013-07-04 13:45:39 UTC
Miguel, it seems this is not working. At least it's not in the test case provided in #12927.

CouchSample has similar code but it's never reached/executed at runtime. In fact the demo does nothing but show "Welcome". I never tested it before so I'm not sure what to expect - but I assume there should be more :)
Comment 16 Miguel de Icaza [MSFT] 2013-07-05 10:37:16 UTC

I suspect we need to review the code to make this work.

I think I implemented the current support, and was waiting on someone else to test it, and that never happened.  Can you take a look at it?
Comment 17 Sebastien Pouliot 2013-07-05 11:02:17 UTC
It does not look the generated bindings have any extra code coming from

so it cannot support the feature. That might have broke recently... as it's not a common pattern.
Comment 18 Sebastien Pouliot 2013-07-05 12:00:08 UTC
My bad, generated code is present (in another directory).
Comment 19 Sebastien Pouliot 2013-07-05 15:49:57 UTC
The `MarshalDirectiveException` (see test case from bug #12927) is becasue it's not a type we can marshal. 

However replacing it with an IntPtr (and adding the rebuilding the test case) only move us a bit forward - into a crash inside the wrapper (see below). Note that the same exception/crash happens with, or without, a [MonoNativeFunctionWrapper] on the delegate.

Zoltan do you know any other code using this ? or if a unit test exists ? I'm not sure if the feature regressed (or never worked for a case like this).

System.NullReferenceException: Object reference not set to an instance of an object
  at at (wrapper managed-to-native) object:wrapper_native_0x5 (intptr)
  at TestProject.TestProjectViewController.executeMyBlock (MyBindingProject.myBlockToRecall myBlockToRecall) [0x0000c] in /Users/sebastienpouliot/Downloads/BindingTests/BindingProject/TestProject/TestProjectViewController.cs:42
  at MonoTouch.Trampolines+SDmyBlock.TmyBlock (IntPtr block, IntPtr myBlockToRecall) [0x00000] in <filename unknown>:0
  at at (wrapper native-to-managed) MonoTouch.Trampolines/SDmyBlock:TmyBlock (intptr,intptr)
  at at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:int_objc_msgSend_IntPtr (intptr,intptr,intptr)
  at MyBindingProject.MyObjectiveCProject.MyTestMethod (MyBindingProject.myBlock myBlock) [0x00000] in <filename unknown>:0
  at TestProject.TestProjectViewController.ViewDidLoad () [0x0000d] in /Users/sebastienpouliot/Downloads/BindingTests/BindingProject/TestProject/TestProjectViewController.cs:28
  at at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging:void_objc_msgSend (intptr,intptr)
  at MonoTouch.UIKit.UIWindow.MakeKeyAndVisible () [0x00010] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIWindow.g.cs:129
  at TestProject.AppDelegate.FinishedLaunching (MonoTouch.UIKit.UIApplication app, MonoTouch.Foundation.NSDictionary options) [0x00032] in /Users/sebastienpouliot/Downloads/BindingTests/BindingProject/TestProject/AppDelegate.cs:31
  at at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
  at TestProject.Application.Main (System.String[] args) [0x00001] in /Users/sebastienpouliot/Downloads/BindingTests/BindingProject/TestProject/Main.cs:16

crashing the runtime:

Native stacktrace:

mono-rt: 	0   TestProject                         0x000b6b7d mono_handle_native_sigsegv + 349

mono-rt: 	1   TestProject                         0x000c1d9a sigabrt_signal_handler + 122

mono-rt: 	2   libsystem_c.dylib                   0x059e98cb _sigtramp + 43

mono-rt: 	3   ???                                 0xffffffff 0x0 + 4294967295

mono-rt: 	4   libsystem_sim_c.dylib               0x05783f1c abort + 127

mono-rt: 	5   TestProject                         0x0022ee33 monotouch_unhandled_exception_handler + 291

mono-rt: 	6   TestProject                         0x000b73ab mono_invoke_unhandled_exception_hook + 91

mono-rt: 	7   TestProject                         0x000b63e4 mono_handle_exception_internal + 3812

mono-rt: 	8   TestProject                         0x000b54f9 mono_handle_exception + 41

mono-rt: 	9   TestProject                         0x0005cfb4 altstack_handle_and_restore + 148

mono-rt: 	10  ???                                 0x0f7ed25e 0x0 + 259969630

mono-rt: 	11  ???                                 0x0f7ed343 0x0 + 259969859

mono-rt: 	12  ???                                 0x0f7ecae8 0x0 + 259967720

mono-rt: 	13  ???                                 0x0f7ec6dc 0x0 + 259966684

mono-rt: 	14  TestProject                         0x000085d4 -[MyObjectiveCProject myTestMethod:] + 52

mono-rt: 	15  ???                                 0x0f7ec86a 0x0 + 259967082

mono-rt: 	16  ???                                 0x0f7eba68 0x0 + 259963496

mono-rt: 	17  ???                                 0x0f7eb40f 0x0 + 259961871

mono-rt: 	18  ???                                 0x0e7c9b4d 0x0 + 243047245

mono-rt: 	19  TestProject                         0x000e63b8 mono_jit_runtime_invoke + 744

mono-rt: 	20  TestProject                         0x0017fd5f mono_runtime_invoke + 127

mono-rt: 	21  TestProject                         0x00227eba monotouch_trampoline + 3722

mono-rt: 	22  UIKit                               0x015649fc -[UIViewController loadViewIfRequired] + 696

mono-rt: 	23  UIKit                               0x01564c98 -[UIViewController view] + 35

mono-rt: 	24  UIKit                               0x01499f99 -[UIWindow addRootViewControllerViewIfPossible] + 66

mono-rt: 	25  UIKit                               0x0149a334 -[UIWindow _setHidden:forced:] + 312

mono-rt: 	26  UIKit                               0x0149a59e -[UIWindow _orderFrontWithoutMakingKey] + 49

mono-rt: 	27  UIKit                               0x143bc11b -[UIWindowAccessibility(SafeCategory) _orderFrontWithoutMakingKey] + 77

mono-rt: 	28  UIKit                               0x014a4697 -[UIWindow makeKeyAndVisible] + 65

mono-rt: 	29  ???                                 0x0f7eaf58 0x0 + 259960664

mono-rt: 	30  ???                                 0x0f7eb2d4 0x0 + 259961556

mono-rt: 	31  ???                                 0x0f7e9d3a 0x0 + 259956026

mono-rt: 	32  ???                                 0x0f7e9f0c 0x0 + 259956492

mono-rt: 	33  TestProject                         0x000e63b8 mono_jit_runtime_invoke + 744

mono-rt: 	34  TestProject                         0x0017fd5f mono_runtime_invoke + 127

mono-rt: 	35  TestProject                         0x00227eba monotouch_trampoline + 3722

mono-rt: 	36  UIKit                               0x01459ea9 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 309

mono-rt: 	37  UIKit                               0x0145a6e9 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1536

mono-rt: 	38  UIKit                               0x0145bb5e -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 794

mono-rt: 	39  UIKit                               0x01471a6c -[UIApplication handleEvent:withNewEvent:] + 3447

mono-rt: 	40  UIKit                               0x01471fd9 -[UIApplication sendEvent:] + 85

mono-rt: 	41  UIKit                               0x0145d7d5 _UIApplicationHandleEvent + 736

mono-rt: 	42  GraphicsServices                    0x0651b906 _PurpleEventCallback + 776

mono-rt: 	43  GraphicsServices                    0x0651b411 PurpleEventCallback + 46

mono-rt: 	44  CoreFoundation                      0x04c773e5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53

mono-rt: 	45  CoreFoundation                      0x04c7711b __CFRunLoopDoSource1 + 523

mono-rt: 	46  CoreFoundation                      0x04ca1b30 __CFRunLoopRun + 1552

mono-rt: 	47  CoreFoundation                      0x04ca110d CFRunLoopRunSpecific + 445

mono-rt: 	48  CoreFoundation                      0x04ca0f3b CFRunLoopRunInMode + 123

mono-rt: 	49  UIKit                               0x0145b2b1 -[UIApplication _run] + 822

mono-rt: 	50  UIKit                               0x0145d4eb UIApplicationMain + 1225

mono-rt: 	51  ???                                 0x0f7e620a 0x0 + 259940874

mono-rt: 	52  ???                                 0x0f7e447c 0x0 + 259933308

mono-rt: 	53  ???                                 0x0f7e3f90 0x0 + 259932048

mono-rt: 	54  ???                                 0x0f7e40cf 0x0 + 259932367

mono-rt: 	55  TestProject                         0x000e63b8 mono_jit_runtime_invoke + 744

mono-rt: 	56  TestProject                         0x0017fd5f mono_runtime_invoke + 127

mono-rt: 	57  TestProject                         0x001855d3 mono_runtime_exec_main + 435

mono-rt: 	58  TestProject                         0x00185374 mono_runtime_run_main + 628

mono-rt: 	59  TestProject                         0x0005408d mono_jit_exec + 93

mono-rt: 	60  TestProject                         0x0021ba8a main + 2426

mono-rt: 	61  TestProject                         0x00008611 start + 53
Comment 20 Zoltan Varga 2013-07-05 16:30:35 UTC
The .m file contains this:

    myBlockToRecall toRecallBlock = ^(id object) { return 5; };

The 5 is probably passed back to managed code, which treats it as a C function address, and tries to invoke it.
Comment 21 Rolf Bjarne Kvinge [MSFT] 2013-07-05 19:21:06 UTC
This might be related to the fact that we don't support blocks from Objective-C, only the ones coming from managed code (iirc we assume all blocks come from managed code, and will thus have a certain layout - which is not true for ObjC blocks).
Comment 22 Zack Gramana 2013-09-06 19:59:01 UTC
The issue was present in the original bindings. I committed up some code that will exercise the codepath that triggers the bug: https://github.com/mono/monotouch-bindings/commit/d1b3e26b43e3d2d40af9fd60c07a9252b0fac112.
Comment 23 Zack Gramana 2013-09-09 16:54:46 UTC
The current best test case is the sample solution in the iOS 7 branch of the Couchbaselite bindings: https://github.com/mono/monotouch-bindings/tree/couchbaselite-ios-7/Couchbase/samples/CouchbaseSample.iOS .

The bug is trigger by line RootViewController.cs:93[*]. This line can be exercised by launching the app and adding a new entry.

* https://github.com/mono/monotouch-bindings/blob/couchbaselite-ios-7/Couchbase/samples/CouchbaseSample.iOS/RootViewController.cs#L93
Comment 24 Rolf Bjarne Kvinge [MSFT] 2013-09-26 08:34:40 UTC
The original bindings (from the couchbaselite-ios-7 branch) need to be updated to use the [BlockCallback] attribute:

diff --git a/Couchbase/binding/ApiDefinition.cs b/Couchbase/binding/ApiDefinition.cs
index fca8372..89b62f1 100644
--- a/Couchbase/binding/ApiDefinition.cs
+++ b/Couchbase/binding/ApiDefinition.cs
@@ -12,3 +12,3 @@ namespace Couchbase {
        public delegate bool CBLValidationBlock(CBLRevision newRevision, CBLValidationContext context);
-       public delegate void CBLMapBlock(NSDictionary doc, CBLMapEmitBlock emit);
+       public delegate void CBLMapBlock(NSDictionary doc, [BlockCallback] CBLMapEmitBlock emit);
        public delegate NSObject CBLReduceBlock(NSArray keys, NSArray values, bool rereduce);

With this change the sample solution works fine.