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