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