More embedding samples.
[mono.git] / samples / embed / test-invoke.c
1 #include <mono/jit/jit.h>
2 #include <mono/metadata/environment.h>
3 #include <mono/metadata/assembly.h>
4 #include <mono/metadata/debug-helpers.h>
5 #include <string.h>
6 #include <stdlib.h>
7
8 /*
9  * Simple mono embedding example.
10  * We show how to create objects and invoke methods and set fields in them.
11  * Compile with: 
12  *      gcc -Wall -o test-invoke test-invoke.c `pkg-config --cflags --libs mono` -lm
13  *      mcs invoke.cs
14  * Run with:
15  *      ./test-invoke invoke.exe
16  */
17
18 typedef struct
19 {
20         MonoDomain *domain;
21         const char *file;
22         int argc;
23         char **argv;
24 } MainThreadArgs;
25
26 static void
27 access_valuetype_field (MonoObject *obj)
28 {
29         MonoClass *klass;
30         MonoClassField *field;
31         int val;
32
33         klass = mono_object_get_class (obj);
34
35         /* Now we'll change the value of the 'val' field (see invoke.cs) */
36         field = mono_class_get_field_from_name (klass, "val");
37
38         /* This time we also add a bit of error checking... */
39         if (!field) {
40                 fprintf (stderr, "Can't find field val in MyType\n");
41                 exit (1);
42         }
43         /* Check that val is an int (if you're paranoid or if you need to 
44          * show how this API is used) 
45          */
46         if (mono_type_get_type (mono_field_get_type (field)) != MONO_TYPE_I4) {
47                 fprintf (stderr, "Field val is not a 32 bit integer\n");
48                 exit (1);
49         }
50         
51         /* Note we pass a pointer to the value */
52         mono_field_get_value (obj, field, &val);
53         printf ("Value of field is: %d\n", val);
54         val = 10;
55
56         /* Note we pass a pointer to the value here as well */
57         mono_field_set_value (obj, field, &val);
58
59 }
60
61 static void
62 access_reference_field (MonoObject *obj)
63 {
64         MonoClass *klass;
65         MonoDomain *domain;
66         MonoClassField *str;
67         MonoString *strval;
68         char *p;
69
70         klass = mono_object_get_class (obj);
71         domain = mono_object_get_domain (obj);
72
73         /* Now we'll see that a reference type is handled slightly differently.
74          * First, get the MonoClassField representing it.
75          */
76         str = mono_class_get_field_from_name (klass, "str");
77         
78         /* No change here, we always pass a pointer */
79         mono_field_get_value (obj, str, &strval);
80         
81         /* get the string in UTF-8 encoding to print it */
82         p = mono_string_to_utf8 (strval);
83         printf ("Value of str is: %s\n", p);
84         /* we need to free the result from mono_string_to_utf8 () */
85         g_free (p);
86
87         /* string are immutable, so we need to create a different string */
88         strval = mono_string_new (domain, "hello from the embedding API");
89
90         /* Here is the slight difference: for reference types we pass 
91          * the pointer directly, instead of a pointer to the value.
92          */
93         mono_field_set_value (obj, str, strval);
94
95 }
96
97 /* Demostrate how to call methods */
98 static void
99 call_methods (MonoObject *obj)
100 {
101         MonoClass *klass;
102         MonoDomain *domain;
103         MonoMethod *method = NULL, *m = NULL, *ctor = NULL, *fail = NULL, *mvalues;
104         MonoProperty *prop;
105         MonoObject *result, *exception;
106         MonoString *str;
107         char *p;
108         gpointer iter;
109         gpointer args [2];
110         int val;
111
112         klass = mono_object_get_class (obj);
113         domain = mono_object_get_domain (obj);
114
115         /* retrieve all the methods we need */
116         iter = NULL;
117         while ((m = mono_class_get_methods (klass, &iter))) {
118                 if (strcmp (mono_method_get_name (m), "method") == 0) {
119                         method = m;
120                 } else if (strcmp (mono_method_get_name (m), "Fail") == 0) {
121                         fail = m;
122                 } else if (strcmp (mono_method_get_name (m), "Values") == 0) {
123                         mvalues = m;
124                 } else if (strcmp (mono_method_get_name (m), ".ctor") == 0) {
125                         /* Check it's the ctor that takes two args:
126                          * as you see a contrsuctor is a method like any other.
127                          */
128                         MonoMethodSignature * sig = mono_method_signature (m);
129                         if (mono_signature_get_param_count (sig) == 2) {
130                                 ctor = m;
131                         }
132                 }
133         }
134         /* Now we'll call method () on obj: since it takes no arguments 
135          * we can pass NULL as the third argument to mono_runtime_invoke ().
136          * The method will print the updated value.
137          */
138         mono_runtime_invoke (method, obj, NULL, NULL);
139
140         /* mono_object_new () doesn't call any constructor: this means that
141          * we'll have to invoke the constructor if needed ourselves. Note:
142          * invoking a constructor is no different than calling any other method,
143          * so we'll still call mono_runtime_invoke (). This also means that we 
144          * can invoke a constructor at any time, like now.
145          * First, setup the array of arguments and their values.
146          */
147
148         /* As usual, we use the address of the data for valuetype arguments */
149         val = 7;
150         args [0] = &val;
151         /* and the pointer for reference types: mono_array_new () returns a MonoArray* */
152         args [1] = mono_array_new (domain, mono_get_byte_class (), 256);
153         mono_runtime_invoke (ctor, obj, args, NULL);
154
155         /* A property exists only as a metadata entity, so getting or setting the value
156          * is nothing more than calling mono_runtime_invoke () on the getter or setter method.
157          */
158         prop = mono_class_get_property_from_name (klass, "Value");
159         method = mono_property_get_get_method (prop);
160         result = mono_runtime_invoke (method, obj, NULL, NULL);
161         /* mono_runtime_invoke () always boxes the return value if it's a valuetype */
162         val = *(int*)mono_object_unbox (result);
163         
164         printf ("Value of val from property is: %d\n", val);
165         
166         /* we also have an helper method: note that reference types are returned as is */
167         prop = mono_class_get_property_from_name (klass, "Message");
168         str = (MonoString*)mono_property_get_value (prop, obj, NULL, NULL);
169         /* get the string in UTF-8 encoding to print it */
170         p = mono_string_to_utf8 (str);
171         printf ("Value of str from property is: %s\n", p);
172         /* we need to free the result from mono_string_to_utf8 () */
173         g_free (p);
174
175         /* Now we'll show two things:
176          * 1) static methods are invoked with mono_runtime_invoke () as well,
177          * we just pass NULL as the second argument.
178          * 2) we can catch exceptions thrown by the called method.
179          * Note: fail is declared as static void Fail () in invoke.cs.
180          * We first set result to NULL: if after the invocation it will have
181          * a different value, it will be the exception that was thrown from 
182          * the Fail () method. Note that if an exception was thrown, the return 
183          * value (if any) is undefined and can't be used in any way (yes, the above 
184          * invocations don't have this type of error checking to make things simpler).
185          */
186         exception = NULL;
187         mono_runtime_invoke (fail, NULL, NULL, &exception);
188         if (exception) {
189                 printf ("An exception was thrown in Fail ()\n");
190         }
191
192         /* Now let's see how to handle methods that take by ref arguments:
193          * Valuetypes continue to be passed as pointers to the data.
194          * Reference arguments passed by ref (ref or out is the same)
195          * are handled the same way: a pointer to the pointer is used
196          * (so that the result can be read back).
197          * Small note: in this case (a System.Int32 valuetype) we can just
198          * use &val where val is a C 32 bit integer. In the general case 
199          * unmanaged code doesn't know the size of a valuetype, since the 
200          * runtime may decide to lay it out in what it thinks is a better way 
201          * (unless ExplicitLayout is set). To avoid issues, the best thing is to
202          * create an object of the valuetype's class and retrieve the pointer 
203          * to the data with the mono_object_unbox () function.
204          */
205         val = 100;
206         str = mono_string_new (domain, "another string");
207         args [0] = &val;
208         args [1] = &str;
209         mono_runtime_invoke (mvalues, obj, args, NULL);
210         /* get the string in UTF-8 encoding to print it */
211         p = mono_string_to_utf8 (str);
212         printf ("Values of str/val from Values () are: %s/%d\n", p, val);
213         /* we need to free the result from mono_string_to_utf8 () */
214         g_free (p);
215 }
216
217 static void
218 more_methods (MonoDomain *domain)
219 {
220         MonoClass *klass;
221         MonoMethodDesc* mdesc;
222         MonoMethod *method, *vtmethod;
223         MonoString *str;
224         MonoObject *obj;
225         char *p;
226         int val;
227
228         /* Now let's call an instance method on a valuetype. There are two
229          * different case:
230          * 1) calling a virtual method defined in a base class, like ToString (): 
231          * we need to pass the value boxed in an object
232          * 2) calling a normal instance method: in this case
233          * we pass the address to the valuetype as the second argument 
234          * instead of an object.
235          * First some initialization.
236          */
237         val = 25;
238         klass = mono_get_int32_class ();
239         obj = mono_value_box (domain, klass, &val);
240
241         /* A different way to search for a method */
242         mdesc = mono_method_desc_new (":ToString()", FALSE);
243         vtmethod = mono_method_desc_search_in_class (mdesc, klass);
244
245         str = (MonoString*)mono_runtime_invoke (vtmethod, &val, NULL, NULL);
246         /* get the string in UTF-8 encoding to print it */
247         p = mono_string_to_utf8 (str);
248         printf ("25.ToString (): %s\n", p);
249         /* we need to free the result from mono_string_to_utf8 () */
250         g_free (p);
251
252         /* Now: see how the result is different if we search for the ToString ()
253          * method in System.Object: mono_runtime_invoke () doesn't do any sort of
254          * virtual method invocation: it calls the exact method that it was given 
255          * to execute. If a virtual call is needed, mono_object_get_virtual_method ()
256          * can be called.
257          */
258         method = mono_method_desc_search_in_class (mdesc, mono_get_object_class ());
259         str = (MonoString*)mono_runtime_invoke (method, obj, NULL, NULL);
260         /* get the string in UTF-8 encoding to print it */
261         p = mono_string_to_utf8 (str);
262         printf ("25.ToString (), from System.Object: %s\n", p);
263         /* we need to free the result from mono_string_to_utf8 () */
264         g_free (p);
265
266         /* Now get the method that overrides ToString () in obj */
267         vtmethod = mono_object_get_virtual_method (obj, method);
268         if (mono_class_is_valuetype (mono_method_get_class (vtmethod))) {
269                 printf ("Need to unbox this for call to virtual ToString () for %s\n", mono_class_get_name (klass));
270         }
271
272         mono_method_desc_free (mdesc);
273 }
274
275 static void
276 create_object (MonoDomain *domain, MonoImage *image)
277 {
278         MonoClass *klass;
279         MonoObject *obj;
280
281         klass = mono_class_from_name (image, "Embed", "MyType");
282         if (!klass) {
283                 fprintf (stderr, "Can't find MyType in assembly %s\n", mono_image_get_filename (image));
284                 exit (1);
285         }
286
287         obj = mono_object_new (domain, klass);
288         /* mono_object_new () only allocates the storage: 
289          * it doesn't run any constructor. Tell the runtime to run
290          * the default argumentless constructor.
291          */
292         mono_runtime_object_init (obj);
293
294         access_valuetype_field (obj);
295         access_reference_field (obj);
296
297         call_methods (obj);
298         more_methods (domain);
299 }
300
301 static void main_thread_handler (gpointer user_data)
302 {
303         MainThreadArgs *main_args=(MainThreadArgs *)user_data;
304         MonoAssembly *assembly;
305
306         /* Loading an assembly makes the runtime setup everything
307          * needed to execute it. If we're just interested in the metadata
308          * we'd use mono_image_load (), instead and we'd get a MonoImage*.
309          */
310         assembly = mono_domain_assembly_open (main_args->domain,
311                                               main_args->file);
312         if (!assembly)
313                 exit (2);
314         /*
315          * mono_jit_exec() will run the Main() method in the assembly.
316          * The return value needs to be looked up from
317          * System.Environment.ExitCode.
318          */
319         mono_jit_exec (main_args->domain, assembly, main_args->argc,
320                        main_args->argv);
321
322         create_object (main_args->domain, mono_assembly_get_image (assembly));
323 }
324
325 int 
326 main (int argc, char* argv[]) {
327         MonoDomain *domain;
328         const char *file;
329         int retval;
330         MainThreadArgs main_args;
331         
332         if (argc < 2){
333                 fprintf (stderr, "Please provide an assembly to load\n");
334                 return 1;
335         }
336         file = argv [1];
337         /*
338          * mono_jit_init() creates a domain: each assembly is
339          * loaded and run in a MonoDomain.
340          */
341         domain = mono_jit_init (file);
342
343         main_args.domain=domain;
344         main_args.file=file;
345         main_args.argc=argc-1;
346         main_args.argv=argv+1;
347         
348         mono_runtime_exec_managed_code (domain, main_thread_handler,
349                                         &main_args);
350         
351         retval=mono_environment_exitcode_get ();
352         
353         mono_jit_cleanup (domain);
354         return retval;
355 }
356