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