2002-12-11 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / metadata / appdomain.c
1 /*
2  * appdomain.c: AppDomain functions
3  *
4  * Author:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <string.h>
13
14 #include <mono/os/gc_wrapper.h>
15
16 #include <mono/metadata/object.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/assembly.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/threads.h>
21 #include <mono/metadata/socket-io.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/metadata/gc.h>
24
25 HANDLE mono_delegate_semaphore = NULL;
26 CRITICAL_SECTION mono_delegate_section;
27
28 static MonoObject *
29 mono_domain_transfer_object (MonoDomain *src, MonoDomain *dst, MonoObject *obj);
30
31 static MonoAssembly *
32 mono_domain_assembly_preload (MonoAssemblyName *aname,
33                               gchar **assemblies_path,
34                               gpointer user_data);
35
36 static void
37 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data);
38
39 /*
40  * mono_runtime_init:
41  * @domain: domain returned by mono_init ()
42  *
43  * Initialize the core AppDomain: this function will run also some
44  * IL initialization code, so it needs the execution engine to be fully 
45  * operational.
46  *
47  * AppDomain.SetupInformation is set up in mono_runtime_exec_main, where
48  * we know the entry_assembly.
49  *
50  */
51 void
52 mono_runtime_init (MonoDomain *domain, MonoThreadStartCB start_cb,
53                    MonoThreadStartCB attach_cb)
54 {
55         MonoAppDomainSetup *setup;
56         MonoAppDomain *ad;
57         MonoClass *class;
58         
59         mono_install_assembly_preload_hook (mono_domain_assembly_preload, NULL);
60         mono_install_assembly_load_hook (mono_domain_fire_assembly_load, NULL);
61
62         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomainSetup");
63         setup = (MonoAppDomainSetup *) mono_object_new (domain, class);
64
65         class = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
66         ad = (MonoAppDomain *) mono_object_new (domain, class);
67         ad->data = domain;
68         domain->domain = ad;
69         domain->setup = setup;
70
71         mono_delegate_semaphore = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
72         g_assert (mono_delegate_semaphore != INVALID_HANDLE_VALUE);
73         InitializeCriticalSection (&mono_delegate_section);
74
75         mono_thread_init (domain, start_cb, attach_cb);
76         
77         /* GC init has to happen after thread init */
78         mono_gc_init ();
79
80         mono_network_init ();
81
82         return;
83 }
84
85 void
86 mono_runtime_cleanup (MonoDomain *domain)
87 {
88         mono_thread_cleanup ();
89
90         /* Do this after the thread cleanup, because subthreads might
91          * still be doing socket calls.
92          */
93         mono_network_cleanup ();
94 }
95
96 void
97 ves_icall_System_AppDomainSetup_InitAppDomainSetup (MonoAppDomainSetup *setup)
98 {
99         MonoDomain* domain = mono_domain_get ();
100         MonoAssembly *assembly;
101         gchar *str;
102
103         MONO_ARCH_SAVE_REGS;
104
105         assembly = domain->entry_assembly;
106         g_assert (assembly);
107
108         setup->application_base = mono_string_new (domain, assembly->basedir);
109         str = g_strconcat (assembly->basedir, assembly->aname.name,
110                            ".exe.config", NULL);
111         setup->configuration_file = mono_string_new (domain, str);
112         g_free (str);
113 }
114
115 /*
116  * invokes a method in a specific domain.
117  */
118 static MonoObject*
119 mono_runtime_invoke_in_domain (MonoDomain *domain, MonoMethod *method, void *obj, 
120                                void **params, MonoObject **exc)
121 {
122         MonoDomain *cur = mono_domain_get ();
123         MonoObject **real_exc, *default_exc;
124         MonoObject *res;
125
126         if (domain == cur)
127                 return mono_runtime_invoke (method, obj, params, exc);
128
129         if (!exc)
130                 real_exc = &default_exc;
131         else
132                 real_exc = exc;
133
134         *real_exc = NULL;
135         mono_domain_set (domain);
136         res = mono_runtime_invoke (method, obj, params, real_exc);
137         mono_domain_set (cur);
138
139         if (*real_exc) {
140                 /* transfer Exception to the right domain */
141                 *real_exc = mono_domain_transfer_object (domain, cur, *real_exc);
142
143                 if (!exc)
144                         mono_raise_exception ((MonoException *)*real_exc);
145
146         }
147
148         return res;
149 }
150
151 static MonoObject *
152 mono_domain_transfer_array (MonoDomain *src, MonoDomain *dst, MonoArray *ao)
153 {
154         MonoObject *res = NULL;
155         MonoClass *klass;
156         int esize, ecount, i;
157         guint32 *sizes;
158                 
159         klass = ao->obj.vtable->klass;
160
161         esize = mono_array_element_size (klass);
162         if (ao->bounds == NULL) {
163                 ecount = mono_array_length (ao);
164                 res = (MonoObject *)mono_array_new_full (dst, klass, &ecount, NULL);
165         } else {
166                 sizes = alloca (klass->rank * sizeof(guint32) * 2);
167                 ecount = 1;
168                 for (i = 0; i < klass->rank; ++i) {
169                         sizes [i] = ao->bounds [i].length;
170                         ecount *= ao->bounds [i].length;
171                         sizes [i + klass->rank] = ao->bounds [i].lower_bound;
172                 }
173                 res = (MonoObject *)mono_array_new_full (dst, klass, sizes, sizes + klass->rank);
174         }
175         if (klass->element_class->valuetype) {
176                 if (klass->element_class->blittable) {
177                         memcpy (((MonoArray *)res)->vector, ao->vector, esize * ecount);
178                 } else {
179                         for (i = 0; i < ecount; i++) {
180                                 MonoObject *s, *d;
181                                 gpointer *src_ea = (gpointer *)mono_array_addr_with_size (ao, esize, i);
182                                 gpointer *dst_ea = (gpointer *)mono_array_addr_with_size ((MonoArray *)res, esize, i);
183                                 s = mono_value_box (src, klass->element_class, src_ea);
184                                 d = mono_domain_transfer_object (src, dst, s);
185                                 memcpy (dst_ea, (char *)d + sizeof(MonoObject), esize);
186                         }
187                 }
188         } else {
189                 g_assert (esize == sizeof (gpointer));
190                 for (i = 0; i < ecount; i++) {
191                         gpointer *src_ea = (gpointer *)mono_array_addr_with_size (ao, esize, i);
192                         gpointer *dst_ea = (gpointer *)mono_array_addr_with_size ((MonoArray *)res, esize, i);
193                         *dst_ea = mono_domain_transfer_object (src, dst, *src_ea);
194                 }
195         }
196         return res;
197 }
198
199 /**
200  * mono_domain_transfer_object:
201  * @src: the source domain
202  * @dst: the destination domain
203  * @obj: the object to transfer
204  *
205  * This function is used to transfer objects between domains. This is done by
206  * marshalling or serialisation. 
207  */
208 static MonoObject *
209 mono_domain_transfer_object (MonoDomain *src, MonoDomain *dst, MonoObject *obj)
210 {
211         MonoClass *sic = mono_defaults.serializationinfo_class;
212         MonoClass *klass;
213         MonoObject *res = NULL; 
214         int i;
215
216         if (!obj)
217                 return NULL;
218
219         g_assert (obj->vtable->domain == src);
220
221         klass = obj->vtable->klass;
222
223         /* some special cases */
224         if (klass == mono_defaults.string_class) {
225                 MonoString *str = (MonoString *)obj;
226                 return (MonoObject *)mono_string_new_utf16 (dst, 
227                         (const guint16 *)mono_string_chars (str), str->length); 
228         } 
229
230         if (klass == mono_defaults.monotype_class)
231                 return (MonoObject *) mono_type_get_object (dst, ((MonoReflectionType *)obj)->type);    
232
233         if (MONO_CLASS_IS_ARRAY (klass))
234                 return mono_domain_transfer_array (src, dst, (MonoArray *)obj); 
235
236         if (mono_object_isinst (obj, mono_defaults.iserializeable_class)) {
237                 static MonoMethod *serinfo_ctor1 = NULL, *serinfo_ctor2 = NULL, *get_entries = NULL;
238                 MonoMethod *get_object_data, *ser_ctor;
239                 MonoObject *src_serinfo, *dst_serinfo;
240                 void *pa [2];
241                 MonoStreamingContext ctx;
242                 MonoArray *entries, *trans_entries;
243                 int len;
244
245                 /* get a pointer to the GetObjectData method */ 
246                 get_object_data = klass->vtable [klass->interface_offsets [mono_defaults.iserializeable_class->interface_id]];
247                 g_assert (get_object_data);
248
249                 src_serinfo = mono_object_new (src, sic);
250
251                 if (!serinfo_ctor1) {
252                         for (i = 0; i < sic->method.count; ++i) {
253                                 if (!strcmp (".ctor", sic->methods [i]->name) &&
254                                     sic->methods [i]->signature->param_count == 1) {
255                                         serinfo_ctor1 = sic->methods [i];
256                                         break;
257                                 }
258                         }
259                         g_assert (serinfo_ctor1);
260                 }
261                 
262                 if (!serinfo_ctor2) {
263                         for (i = 0; i < sic->method.count; ++i) {
264                                 if (!strcmp (".ctor", sic->methods [i]->name) &&
265                                     sic->methods [i]->signature->param_count == 2 &&
266                                     sic->methods [i]->signature->params [1]->type == MONO_TYPE_SZARRAY) {
267                                         serinfo_ctor2 = sic->methods [i];
268                                         break;
269                                 }
270                         }
271                         g_assert (serinfo_ctor2);
272                 }
273                 
274                 if (!get_entries) {
275                         for (i = 0; i < sic->method.count; ++i) {
276                                 if (!strcmp ("get_entries", sic->methods [i]->name) &&
277                                     sic->methods [i]->signature->param_count == 0) {
278                                         get_entries = sic->methods [i];
279                                         break;
280                                 }
281                         }
282                         g_assert (get_entries);
283                 }
284
285                 pa [0] = mono_type_get_object (src, &klass->byval_arg);
286                 mono_runtime_invoke_in_domain (src, serinfo_ctor1, src_serinfo, pa, NULL);
287
288                 ctx.additional = NULL;
289                 ctx.state = 128; /* CrossAppDomain */
290                 pa [0] = src_serinfo;
291                 pa [1] = &ctx;
292                 mono_runtime_invoke_in_domain (src, get_object_data, obj, pa, NULL);
293
294                 entries = (MonoArray *)mono_runtime_invoke_in_domain (src, get_entries, src_serinfo, NULL, NULL);
295
296                 g_assert (src_serinfo->vtable->domain == src);
297                 g_assert (entries->obj.vtable->domain == src);
298
299                 /* transfer all SerializationEntry objects */
300                 len = mono_array_length ((MonoArray*)entries);
301                 trans_entries = mono_array_new (dst, entries->obj.vtable->klass->element_class, len);
302
303                 for (i = 0; i < len; i++) {
304                         MonoSerializationEntry *s, *d;
305                         s = (MonoSerializationEntry *)mono_array_addr_with_size (entries, 
306                                 sizeof (MonoSerializationEntry), i);
307                         d = (MonoSerializationEntry *)mono_array_addr_with_size (trans_entries, 
308                                 sizeof (MonoSerializationEntry), i);
309                         d->name = (MonoString *)mono_domain_transfer_object (src, dst, (MonoObject *)s->name);
310                         d->value = mono_domain_transfer_object (src, dst, s->value);
311                         d->type = (MonoReflectionType *)mono_domain_transfer_object (src, dst, (MonoObject *)s->type);
312                 }
313
314                 dst_serinfo = mono_object_new (dst, sic);
315
316                 pa [0] = mono_type_get_object (dst, &klass->byval_arg);
317                 pa [1] = trans_entries;
318                 mono_runtime_invoke_in_domain (dst, serinfo_ctor2, dst_serinfo, pa, NULL);
319
320                 ser_ctor = NULL;
321                 for (i = 0; i < klass->method.count; i++) {
322                         MonoMethod *t = klass->methods [i];
323                         if (!strcmp (t->name, ".ctor") && t->signature->param_count == 2 &&
324                             mono_metadata_type_equal (t->signature->params [0], 
325                                                       &mono_defaults.serializationinfo_class->byval_arg) &&
326                             mono_metadata_type_equal (t->signature->params [1], 
327                                                       &mono_defaults.streamingcontext_class->byval_arg))
328                                 ser_ctor = t;
329                 }
330                 g_assert (ser_ctor);
331
332                 res = mono_object_new (dst, klass);
333
334                 ctx.additional = NULL;
335                 ctx.state = 128; /* CrossAppDomain */
336                 pa [0] = dst_serinfo;
337                 pa [1] = &ctx;
338                 mono_runtime_invoke_in_domain (dst, ser_ctor, res, pa, NULL);
339
340                 return res;
341         }
342
343         if (!(klass->flags & TYPE_ATTRIBUTE_SERIALIZABLE)) {
344                 MonoException *exc = NULL;
345                 char *msg;
346
347                 msg = g_strdup_printf ("klass \"%s.%s\" is not serializable", 
348                                        klass->name_space, klass->name);
349                 exc = mono_get_exception_serialization (msg);
350                 g_free (msg);
351
352                 mono_raise_exception (exc);
353         }
354
355         res = mono_object_new (dst, klass);
356
357         for (i = 0; i < klass->field.count; i++) {
358                 MonoClassField *field = &klass->fields [i];
359                 MonoType *type = field->type;
360                 int size, align;
361                 char *src_ptr, *dst_ptr;
362                 int simple_type;
363
364                 if (type->attrs & FIELD_ATTRIBUTE_STATIC ||
365                     type->attrs & FIELD_ATTRIBUTE_NOT_SERIALIZED)
366                         continue;
367
368                 size = mono_type_size (type, &align);
369
370                 dst_ptr = (char*)res + field->offset;
371                 src_ptr = (char *)obj + field->offset;
372
373                 g_assert (!type->byref);
374                 
375                 simple_type = type->type;
376         handle_enum:
377                 switch (simple_type) {
378                 case MONO_TYPE_BOOLEAN:
379                 case MONO_TYPE_CHAR: 
380                 case MONO_TYPE_I1:
381                 case MONO_TYPE_U1:
382                 case MONO_TYPE_I2:
383                 case MONO_TYPE_U2:
384                 case MONO_TYPE_I4:
385                 case MONO_TYPE_U4:
386                 case MONO_TYPE_I:
387                 case MONO_TYPE_U:
388                 case MONO_TYPE_I8: 
389                 case MONO_TYPE_U8:
390                 case MONO_TYPE_R4:
391                 case MONO_TYPE_R8:
392                         memcpy (dst_ptr, src_ptr, size);
393                         break;
394                 case MONO_TYPE_OBJECT:
395                 case MONO_TYPE_ARRAY:
396                 case MONO_TYPE_SZARRAY:
397                 case MONO_TYPE_CLASS:
398                 case MONO_TYPE_STRING: {
399                         *(MonoObject **)dst_ptr = mono_domain_transfer_object (src, dst, *(MonoObject **)src_ptr);
400                         break;
401                 }
402                 case MONO_TYPE_VALUETYPE: {
403                         MonoObject *boxed_src, *tmp;
404
405                         if (type->data.klass->enumtype) {
406                                 simple_type = type->data.klass->enum_basetype->type;
407                                 goto handle_enum;
408                         }
409                         boxed_src = mono_value_box (src, type->data.klass, src_ptr);
410                         tmp = mono_domain_transfer_object (src, dst, boxed_src);
411                         memcpy (dst_ptr, (char *)tmp + sizeof (MonoObject), size);
412                         break;
413                 }
414                 default:
415                         g_assert_not_reached ();
416                 }
417         }
418
419         return res;
420 }
421
422 MonoObject *
423 ves_icall_System_AppDomain_GetData (MonoAppDomain *ad, MonoString *name)
424 {
425         MonoDomain *add = ad->data;
426         MonoDomain *cur = mono_domain_get ();
427         MonoObject *o;
428         char *str;
429
430         MONO_ARCH_SAVE_REGS;
431
432         g_assert (ad != NULL);
433         g_assert (name != NULL);
434
435         str = mono_string_to_utf8 (name);
436
437         mono_domain_lock (add);
438         if (!strcmp (str, "APPBASE"))
439                 o = (MonoObject *)add->setup->application_base;
440         else if (!strcmp (str, "APP_CONFIG_FILE"))
441                 o = (MonoObject *)add->setup->configuration_file;
442         else if (!strcmp (str, "DYNAMIC_BASE"))
443                 o = (MonoObject *)add->setup->dynamic_base;
444         else if (!strcmp (str, "APP_NAME"))
445                 o = (MonoObject *)add->setup->application_name;
446         else if (!strcmp (str, "CACHE_BASE"))
447                 o = (MonoObject *)add->setup->cache_path;
448         else if (!strcmp (str, "PRIVATE_BINPATH"))
449                 o = (MonoObject *)add->setup->private_bin_path;
450         else if (!strcmp (str, "BINPATH_PROBE_ONLY"))
451                 o = (MonoObject *)add->setup->private_bin_path_probe;
452         else if (!strcmp (str, "SHADOW_COPY_DIRS"))
453                 o = (MonoObject *)add->setup->shadow_copy_directories;
454         else if (!strcmp (str, "FORCE_CACHE_INSTALL"))
455                 o = (MonoObject *)add->setup->shadow_copy_files;
456         else 
457                 o = mono_g_hash_table_lookup (add->env, name);
458
459         mono_domain_unlock (add);
460         g_free (str);
461
462         if (!o)
463                 return NULL;
464
465         return mono_domain_transfer_object (add, cur, o);
466 }
467
468 void
469 ves_icall_System_AppDomain_SetData (MonoAppDomain *ad, MonoString *name, MonoObject *data)
470 {
471         MonoDomain *add = ad->data;
472         MonoDomain *cur = mono_domain_get ();
473         MonoObject *o;
474
475         MONO_ARCH_SAVE_REGS;
476
477         g_assert (ad != NULL);
478         g_assert (name != NULL);
479
480         o = mono_domain_transfer_object (cur, add, data);
481
482         mono_domain_lock (add);
483         mono_g_hash_table_insert (add->env, name, o);
484
485         mono_domain_unlock (add);
486 }
487
488 MonoAppDomainSetup *
489 ves_icall_System_AppDomain_getSetup (MonoAppDomain *ad)
490 {
491         MONO_ARCH_SAVE_REGS;
492
493         g_assert (ad != NULL);
494         g_assert (ad->data != NULL);
495
496         return ad->data->setup;
497 }
498
499 MonoString *
500 ves_icall_System_AppDomain_getFriendlyName (MonoAppDomain *ad)
501 {
502         MONO_ARCH_SAVE_REGS;
503
504         g_assert (ad != NULL);
505         g_assert (ad->data != NULL);
506
507         return mono_string_new (ad->data, ad->data->friendly_name);
508 }
509
510 MonoAppDomain *
511 ves_icall_System_AppDomain_getCurDomain ()
512 {
513         MonoDomain *add = mono_domain_get ();
514
515         MONO_ARCH_SAVE_REGS;
516
517         return add->domain;
518 }
519
520 MonoAppDomain *
521 ves_icall_System_AppDomain_createDomain (MonoString *friendly_name, MonoAppDomainSetup *setup)
522 {
523         MonoDomain *domain = mono_domain_get (); 
524         MonoClass *adclass;
525         MonoAppDomain *ad;
526         MonoDomain *data;
527         
528         MONO_ARCH_SAVE_REGS;
529
530         adclass = mono_class_from_name (mono_defaults.corlib, "System", "AppDomain");
531         
532         /* FIXME: pin all those objects */
533         ad = (MonoAppDomain *) mono_object_new (domain, adclass);
534         ad->data = data = mono_domain_create ();
535         data->domain = ad;
536         data->setup = setup;
537         data->friendly_name = mono_string_to_utf8 (friendly_name);
538
539         /* FIXME: what to do next ? */
540
541         return ad;
542 }
543
544 typedef struct {
545         MonoArray *res;
546         MonoDomain *domain;
547         int idx;
548 } add_assembly_helper_t;
549
550 static void
551 add_assembly (gpointer key, gpointer value, gpointer user_data)
552 {
553         add_assembly_helper_t *ah = (add_assembly_helper_t *) user_data;
554
555         mono_array_set (ah->res, gpointer, ah->idx++, mono_assembly_get_object (ah->domain, value));
556 }
557
558 MonoArray *
559 ves_icall_System_AppDomain_GetAssemblies (MonoAppDomain *ad)
560 {
561         MonoDomain *domain = ad->data; 
562         static MonoClass *System_Reflection_Assembly;
563         MonoArray *res;
564         add_assembly_helper_t ah;
565         
566         MONO_ARCH_SAVE_REGS;
567
568         if (!System_Reflection_Assembly)
569                 System_Reflection_Assembly = mono_class_from_name (
570                         mono_defaults.corlib, "System.Reflection", "Assembly");
571
572         res = mono_array_new (domain, System_Reflection_Assembly, g_hash_table_size (domain->assemblies));
573
574         ah.domain = domain;
575         ah.res = res;
576         ah.idx = 0;
577         mono_domain_lock (domain);
578         g_hash_table_foreach (domain->assemblies, add_assembly, &ah);
579         mono_domain_unlock (domain);
580
581         return res;
582 }
583
584 /*
585  * Used to find methods in AppDomain class.
586  * It only works if there are no multiple signatures for any given method name
587  */
588 static MonoMethod *
589 look_for_method_by_name (MonoClass *klass, const gchar *name)
590 {
591         gint i;
592         MonoMethod *method;
593
594         for (i = 0; i < klass->method.count; i++) {
595                 method = klass->methods [i];
596                 if (!strcmp (method->name, name))
597                         return method;
598         }
599
600         return NULL;
601 }
602
603 static MonoReflectionAssembly *
604 try_assembly_resolve (MonoDomain *domain, MonoString *fname)
605 {
606         MonoClass *klass;
607         MonoMethod *method;
608         void *params [1];
609
610         g_assert (domain != NULL && fname != NULL);
611
612         klass = domain->domain->object.vtable->klass;
613         g_assert (klass);
614         
615         method = look_for_method_by_name (klass, "DoAssemblyResolve");
616         if (method == NULL) {
617                 g_warning ("Method AppDomain.DoAssemblyResolve not found.\n");
618                 return NULL;
619         }
620
621         *params = fname;
622         return (MonoReflectionAssembly *) mono_runtime_invoke (method, domain->domain, params, NULL);
623 }
624
625 static void
626 add_assemblies_to_domain (MonoDomain *domain, MonoAssembly *ass)
627 {
628         gint i;
629
630         if (g_hash_table_lookup (domain->assemblies, ass->aname.name))
631                 return; /* This is ok while no lazy loading of assemblies */
632
633         mono_domain_lock (domain);
634         g_hash_table_insert (domain->assemblies, (gpointer) ass->aname.name, ass);
635         mono_domain_unlock (domain);
636
637         for (i = 0; ass->image->references [i] != NULL; i++)
638                 add_assemblies_to_domain (domain, ass->image->references [i]);
639 }
640
641 static void
642 mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
643 {
644         MonoDomain *domain = mono_domain_get ();
645         MonoReflectionAssembly *ref_assembly;
646         MonoClass *klass;
647         MonoMethod *method;
648         void *params [1];
649
650         klass = domain->domain->object.vtable->klass;
651         
652         method = look_for_method_by_name (klass, "DoAssemblyLoad");
653         if (method == NULL) {
654                 g_warning ("Method AppDomain.DoAssemblyLoad not found.\n");
655                 return;
656         }
657
658         add_assemblies_to_domain (domain, assembly);
659
660         ref_assembly = mono_assembly_get_object (domain, assembly);
661         g_assert (ref_assembly);
662
663         *params = ref_assembly;
664         mono_runtime_invoke (method, domain->domain, params, NULL);
665 }
666
667 static void
668 set_domain_search_path (MonoDomain *domain)
669 {
670         MonoAppDomainSetup *setup;
671         gchar **tmp;
672         gchar *utf8;
673         gint i;
674         gint npaths = 0;
675         gchar **pvt_split = NULL;
676
677         if (domain->search_path != NULL)
678                 return;
679
680         setup = domain->setup;
681         if (setup->application_base)
682                 npaths++;
683
684         if (setup->private_bin_path) {
685                 utf8 = mono_string_to_utf8 (setup->private_bin_path);
686                 pvt_split = g_strsplit (utf8, G_SEARCHPATH_SEPARATOR_S, 1000);
687                 g_free (utf8);
688                 for (tmp = pvt_split; *tmp; tmp++, npaths++);
689         }
690
691         if (!npaths) {
692                 if (pvt_split)
693                         g_strfreev (pvt_split);
694                 /*
695                  * Don't do this because the first time is called, the domain
696                  * setup is not finished.
697                  *
698                  * domain->search_path = g_malloc (sizeof (char *));
699                  * domain->search_path [0] = NULL;
700                 */
701                 return;
702         }
703
704         domain->search_path = tmp = g_malloc ((npaths + 1) * sizeof (gchar *));
705         tmp [npaths] = NULL;
706         if (setup->application_base) {
707                 *tmp = mono_string_to_utf8 (setup->application_base);
708         } else {
709                 *tmp = g_strdup ("");
710         }
711
712         tmp++;
713         npaths--;
714         for (i = 0; i < npaths; i++)
715                 tmp [i] = pvt_split [i];
716
717         g_strfreev (pvt_split);
718 }
719
720 static MonoAssembly *
721 real_load (gchar **search_path, gchar *filename)
722 {
723         MonoAssembly *result;
724         gchar **path;
725         gchar *fullpath;
726
727         for (path = search_path; *path; path++) {
728                 if (**path == '\0')
729                         continue; /* Ignore empty ApplicationBase */
730                 fullpath = g_build_filename (*path, filename, NULL);
731                 result = mono_assembly_open (fullpath, NULL);
732                 g_free (fullpath);
733                 if (result)
734                         return result;
735         }
736
737         return NULL;
738 }
739
740 /*
741  * Try loading the assembly from ApplicationBase and PrivateBinPath 
742  * and then from assemblies_path if any.
743  */
744 static MonoAssembly *
745 mono_domain_assembly_preload (MonoAssemblyName *aname,
746                               gchar **assemblies_path,
747                               gpointer user_data)
748 {
749         MonoDomain *domain = mono_domain_get ();
750         MonoAssembly *result;
751         gchar *dll, *exe;
752
753         set_domain_search_path (domain);
754
755         dll = g_strconcat (aname->name, ".dll", NULL);
756         exe = g_strdup (dll);
757         strcpy (exe + strlen (exe) - 4, ".exe");
758
759         if (domain->search_path && domain->search_path [0] != NULL) {
760                 /* TODO: should also search in name/name.dll and name/name.exe from appbase */
761                 result = real_load (domain->search_path, dll);
762                 if (result) {
763                         g_free (dll);
764                         g_free (exe);
765                         return result;
766                 }
767
768                 result = real_load (domain->search_path, exe);
769                 if (result) {
770                         g_free (dll);
771                         g_free (exe);
772                         return result;
773                 }
774         }
775
776         if (assemblies_path && assemblies_path [0] != NULL) {
777                 result = real_load (assemblies_path, dll);
778                 if (result) {
779                         g_free (dll);
780                         g_free (exe);
781                         return result;
782                 }
783
784                 result = real_load (assemblies_path, exe);
785                 if (result) {
786                         g_free (dll);
787                         g_free (exe);
788                         return result;
789                 }
790         }
791         
792         g_free (dll);
793         g_free (exe);
794         return NULL;
795 }
796
797 MonoReflectionAssembly *
798 ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname)
799 {
800         MonoDomain *domain = mono_domain_get ();
801         char *name, *filename;
802         MonoImageOpenStatus status = MONO_IMAGE_OK;
803         MonoAssembly *ass;
804
805         MONO_ARCH_SAVE_REGS;
806
807         if (fname == NULL) {
808                 MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
809                 mono_raise_exception (exc);
810         }
811                 
812         name = filename = mono_string_to_utf8 (fname);
813
814         /* FIXME: move uri handling to mono_assembly_open */
815         if (strncmp (filename, "file://", 7) == 0)
816                 filename += 7;
817
818         ass = mono_assembly_open (filename, &status);
819         
820         g_free (name);
821
822         if (!ass){
823                 MonoException *exc = mono_get_exception_file_not_found (fname);
824                 mono_raise_exception (exc);
825         }
826
827         return mono_assembly_get_object (domain, ass);
828 }
829
830 static void
831 free_assembly_name (MonoAssemblyName *aname)
832 {
833         if (aname == NULL)
834                 return;
835
836         g_free ((void *) aname->name);
837         g_free ((void *) aname->culture);
838         g_free ((void *) aname->hash_value);
839 }
840
841 static gboolean
842 get_info_from_assembly_name (MonoReflectionAssemblyName *assRef, MonoAssemblyName *aname)
843 {
844         gchar *name;
845         gchar *value;
846         gchar **parts;
847         gchar **tmp;
848         gint major, minor, build, revision;
849
850         memset (aname, 0, sizeof (MonoAssemblyName));
851
852         name = mono_string_to_utf8 (assRef->name);
853         parts = tmp = g_strsplit (name, ",", 4);
854         g_free (name);
855         if (!tmp || !*tmp) {
856                 g_strfreev (tmp);
857                 return FALSE;
858         }
859
860         value = g_strstrip (*tmp);
861         /* g_print ("Assembly name: %s\n", value); */
862         aname->name = g_strdup (value);
863         tmp++;
864         if (!*tmp) {
865                 g_strfreev (parts);
866                 return TRUE;
867         }
868
869         value = g_strstrip (*tmp);
870         if (strncmp (value, "Version=", 8)) {
871                 g_strfreev (parts);
872                 return FALSE;
873         }
874         
875         if (sscanf (value + 8, "%u.%u.%u.%u", &major, &minor, &build, &revision) != 4) {
876                 g_strfreev (parts);
877                 return FALSE;
878         }
879
880         /* g_print ("Version: %u.%u.%u.%u\n", major, minor, build, revision); */
881         aname->major = major;
882         aname->minor = minor;
883         aname->build = build;
884         aname->revision = revision;
885         tmp++;
886
887         if (!*tmp) {
888                 g_strfreev (parts);
889                 return FALSE;
890         }
891
892         value = g_strstrip (*tmp);
893         if (strncmp (value, "Culture=", 8)) {
894                 g_strfreev (parts);
895                 return FALSE;
896         }
897
898         /* g_print ("Culture: %s\n", aname->culture); */
899         aname->culture = g_strstrip (g_strdup (value + 8));
900         tmp++;
901
902         if (!*tmp) {
903                 g_strfreev (parts);
904                 return FALSE;
905         }
906
907         value = g_strstrip (*tmp);
908         if (strncmp (value, "PublicKeyToken=", 15)) {
909                 g_strfreev (parts);
910                 return FALSE;
911         }
912
913         value += 15;
914         if (*value && strcmp (value, "null")) {
915                 gint i, len;
916                 gchar h, l;
917                 gchar *result;
918                 
919                 value = g_strstrip (g_strdup (value));
920                 len = strlen (value);
921                 if (len % 2) {
922                         g_strfreev (parts);
923                         return FALSE;
924                 }
925                 
926                 aname->hash_len = len / 2;
927                 aname->hash_value = g_malloc0 (aname->hash_len);
928                 result = (gchar *) aname->hash_value;
929                 
930                 for (i = 0; i < len; i++) {
931                         if (i % 2) {
932                                 l = g_ascii_xdigit_value (value [i]);
933                                 if (l == -1) {
934                                         g_strfreev (parts);
935                                         return FALSE;
936                                 }
937                                 result [i / 2] = (h * 16) + l;
938                         } else {
939                                 h = g_ascii_xdigit_value (value [i]);
940                                 if (h == -1) {
941                                         g_strfreev (parts);
942                                         return FALSE;
943                                 }
944                         }
945                 }
946
947                 /*
948                 g_print ("PublicKeyToken: ");
949                 for (i = 0; i < aname->hash_len; i++) {
950                         g_print ("%x", 0x00FF & aname->hash_value [i]); 
951                 }
952                 g_print ("\n");
953                 */
954         }
955
956         g_strfreev (parts);
957         return TRUE;
958 }
959
960 MonoReflectionAssembly *
961 ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoReflectionAssemblyName *assRef, MonoObject *evidence)
962 {
963         MonoDomain *domain = ad->data; 
964         MonoImageOpenStatus status = MONO_IMAGE_OK;
965         MonoAssembly *ass;
966         MonoAssemblyName aname;
967         MonoReflectionAssembly *refass = NULL;
968
969         MONO_ARCH_SAVE_REGS;
970
971         memset (&aname, 0, sizeof (aname));
972
973         /* FIXME : examine evidence? */
974
975         g_assert (assRef != NULL);
976         g_assert (assRef->name != NULL);
977
978         if (!get_info_from_assembly_name (assRef, &aname)) {
979                 MonoException *exc;
980
981                 free_assembly_name (&aname);
982                 /* This is a parse error... */
983                 exc = mono_get_exception_file_not_found (assRef->name);
984                 mono_raise_exception (exc);
985         }
986
987         ass = mono_assembly_load (&aname, NULL, &status);
988         free_assembly_name (&aname);
989
990         if (!ass && (refass = try_assembly_resolve (domain, assRef->name)) == NULL){
991                 /* FIXME: it doesn't make much sense since we really don't have a filename ... */
992                 MonoException *exc = mono_get_exception_file_not_found (assRef->name);
993                 mono_raise_exception (exc);
994         }
995
996         if (refass != NULL)
997                 return refass;
998
999         return mono_assembly_get_object (domain, ass);
1000 }
1001
1002 void
1003 ves_icall_System_AppDomain_Unload (MonoAppDomain *ad)
1004 {
1005         MONO_ARCH_SAVE_REGS;
1006
1007         mono_domain_unload (ad->data, FALSE);
1008 }
1009
1010 gint32
1011 ves_icall_System_AppDomain_ExecuteAssembly (MonoAppDomain *ad, MonoString *file, 
1012                                             MonoObject *evidence, MonoArray *args)
1013 {
1014         MonoDomain *cdom = mono_domain_get ();
1015         MonoAssembly *assembly;
1016         MonoImage *image;
1017         MonoMethod *method;
1018         MonoObject *margs;
1019         char *filename;
1020         gint32 res;
1021
1022         MONO_ARCH_SAVE_REGS;
1023
1024         mono_domain_set (ad->data);
1025
1026         filename = mono_string_to_utf8 (file);
1027         assembly = mono_assembly_open (filename, NULL);
1028         g_free (filename);
1029
1030         if (!assembly) {
1031                 mono_raise_exception ((MonoException *)mono_exception_from_name (
1032                         mono_defaults.corlib, "System.IO", "FileNotFoundException"));
1033         }
1034
1035         image = assembly->image;
1036
1037         method = mono_get_method (image, mono_image_get_entry_point (image), NULL);
1038
1039         if (!method)
1040                 g_error ("No entry point method found in %s", image->name);
1041
1042         margs = mono_domain_transfer_object (cdom, ad->data, (MonoObject *)args);
1043         if (!margs)
1044                 margs = (MonoObject *) mono_array_new (ad->data, mono_defaults.string_class, 0);
1045         res = mono_runtime_exec_main (method, (MonoArray *)margs, NULL);
1046
1047         mono_domain_set (cdom);
1048
1049         return res;
1050 }
1051