viernes, febrero 11, 2005

Problems with the C Mono api

I spent almost a day solving a small problem I had creating structs from the C Mono api. When I returned this instance of struct, an exception was thrown, advising that it was not having a value...

Struct types in Mono are value based types, and since that, they cannot be null. Based in this, I could not figure out the reason for the exception. Then I remembered a similar problem I had when I defined a function, returning a MonoObject * (MonoObject* is the representation of the object C# type), that did not return the correct value (I forgot to add the return statement).

To create an instance in the C api, the class must be passed (a look into mono/samples/embed is a good beginning to begin to understand the C api), which is a MonoClass*. So, with classes you do:

MonoObject *obj;
/* mono_object_new (MonoDomain *d, MonoClass *klass)
* receives as first arg the current domain and a pointer to
* a defined class */
obj = mono_object_new (mono_domain_get (), klass);

/* Constructors must be called (they are common MonoMethod*'s) after the object is
* created; basically, with mono_object_new the object is only allocated.
* Note: mono_runtime_invoke (MonoMethod*, MonoObject*,
* void **params, MonoObject **exception)
mono_runtime_invoke (ctor, obj, params, NULL);

/* Return a fresh instance, with its constructor called */
return obj;

But..., there is the trick for structs (I had to go and read the code of Reflection after a day suffering. Mental note: remember to go to check the code when having a weird error). The trick here, is to unbox the struct before calling the constructor. Structs are allocated as value types, and finishing the stack frame where they were defined (the function itself), they dissapear. Boxing refers to the process of allocating a structure (and anything value based) as a reference type. Unbox is the oposite.

So, we would have this code for structs:

/* First, create the instance, boxed */
obj = mono_object_new (mono_domain_get (), klass);

/* Unbox on a void* variable */
o = mono_object_unbox (obj);

/* Call the method/constructor on the unboxed */
mono_runtime_invoke (ctor, o, params, NULL);

/* But ... return the MonoObject* representation */
return obj;

The question here is: structs created from the C Mono api must be created using mono_object_new, which creates boxed structs? The answer is: yes. The runtime can know the times of structs, but, the hacker would not be able to know this, even at compile time.

I hope this helps anybody (in the future, mostly) to avoid this problem.

1 comentario:

Carlos Alberto Cortez dijo...

Well, I hope to commit the changes to begin the support of ReflectionOnly api.

In that case, I'll be also posting some details not found in other place in internet ;-)