2002-07-15 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / mono / metadata / object.c
1 /*
2  * object.c: Object creation for the Mono runtime
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9 #include <config.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <mono/metadata/mono-endian.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/metadata/tokentype.h>
17 #include <mono/metadata/loader.h>
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/gc.h>
20 #include <mono/metadata/appdomain.h>
21 #include <mono/metadata/assembly.h>
22 #include <mono/metadata/threadpool.h>
23 #include "mono/metadata/debug-helpers.h"
24 #if HAVE_BOEHM_GC
25 #include <gc/gc.h>
26 #endif
27
28 void
29 mono_runtime_object_init (MonoObject *this)
30 {
31         int i;
32         MonoMethod *method = NULL;
33         MonoClass *klass = this->vtable->klass;
34
35         for (i = 0; i < klass->method.count; ++i) {
36                 if (!strcmp (".ctor", klass->methods [i]->name) &&
37                     klass->methods [i]->signature->param_count == 0) {
38                         method = klass->methods [i];
39                         break;
40                 }
41         }
42
43         g_assert (method);
44
45         mono_runtime_invoke (method, this, NULL, NULL);
46 }
47
48 /*
49  * runtime_class_init:
50  * @klass: klass that needs to be initialized
51  *
52  * This routine calls the class constructor for @class.
53  */
54 void
55 mono_runtime_class_init (MonoClass *klass)
56 {
57         int i;
58
59         for (i = 0; i < klass->method.count; ++i) {
60                 MonoMethod *method = klass->methods [i];
61                 if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && 
62                     (strcmp (".cctor", method->name) == 0)) {
63                         mono_runtime_invoke (method, NULL, NULL, NULL);
64                         return;
65                 }
66         }
67
68         /* No class constructor found */
69 }
70
71 static gpointer
72 default_trampoline (MonoMethod *method)
73 {
74         return method;
75 }
76
77 static gpointer
78 default_remoting_trampoline (MonoMethod *method)
79 {
80         g_error ("remoting not installed");
81         return NULL;
82 }
83
84 static MonoTrampoline arch_create_jit_trampoline = default_trampoline;
85 static MonoTrampoline arch_create_remoting_trampoline = default_remoting_trampoline;
86
87 void
88 mono_install_trampoline (MonoTrampoline func) 
89 {
90         arch_create_jit_trampoline = func? func: default_trampoline;
91 }
92
93 void
94 mono_install_remoting_trampoline (MonoTrampoline func) 
95 {
96         arch_create_remoting_trampoline = func? func: default_remoting_trampoline;
97 }
98
99 static MonoCompileFunc default_mono_compile_method = NULL;
100
101 void        
102 mono_install_compile_method (MonoCompileFunc func)
103 {
104         default_mono_compile_method = func;
105 }
106
107 gpointer 
108 mono_compile_method (MonoMethod *method)
109 {
110         if (!default_mono_compile_method) {
111                 g_error ("compile method called on uninitialized runtime");
112                 return NULL;
113         }
114         return default_mono_compile_method (method);
115 }
116
117
118 #if 0 && HAVE_BOEHM_GC
119 static void
120 vtable_finalizer (void *obj, void *data) {
121         g_print ("%s finalized (%p)\n", (char*)data, obj);
122 }
123 #endif
124
125 /**
126  * mono_class_vtable:
127  * @domain: the application domain
128  * @class: the class to initialize
129  *
130  * VTables are domain specific because we create domain specific code, and 
131  * they contain the domain specific static class data.
132  */
133 MonoVTable *
134 mono_class_vtable (MonoDomain *domain, MonoClass *class)
135 {
136         MonoClass *k;
137         MonoVTable *vt;
138         MonoClassField *field;
139         guint32 cindex;
140         guint32 cols [MONO_CONSTANT_SIZE];
141         const char *p;
142         char *t;
143         int i, len;
144
145         g_assert (class);
146
147         /* can interfaces have static fields? */
148         if (class->flags & TYPE_ATTRIBUTE_INTERFACE)
149                 g_assert_not_reached ();
150
151         mono_domain_lock (domain);
152         if ((vt = mono_g_hash_table_lookup (domain->class_vtable_hash, class))) {
153                 mono_domain_unlock (domain);
154                 return vt;
155         }
156         
157         if (!class->inited)
158                 mono_class_init (class);
159
160         mono_stats.used_class_count++;
161         mono_stats.class_vtable_size += sizeof (MonoVTable) + class->vtable_size * sizeof (gpointer);
162
163         vt = mono_mempool_alloc0 (domain->mp,  sizeof (MonoVTable) + 
164                                   class->vtable_size * sizeof (gpointer));
165         vt->klass = class;
166         vt->domain = domain;
167
168         if (class->class_size) {
169 #if HAVE_BOEHM_GC
170                 vt->data = GC_malloc (class->class_size + 8);
171                 /*vt->data = GC_debug_malloc (class->class_size + 8, class->name, 2);*/
172                 /*GC_register_finalizer (vt->data, vtable_finalizer, class->name, NULL, NULL);*/
173                 mono_g_hash_table_insert (domain->static_data_hash, class, vt->data);
174 #else
175                 vt->data = mono_mempool_alloc0 (domain->mp, class->class_size + 8);
176                 
177 #endif
178                 mono_stats.class_static_data_size += class->class_size + 8;
179         }
180
181         for (i = class->field.first; i < class->field.last; ++i) {
182                 field = &class->fields [i - class->field.first];
183                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
184                         continue;
185                 if ((field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA)) {
186                         MonoClass *fklass = mono_class_from_mono_type (field->type);
187                         t = (char*)vt->data + field->offset;
188                         g_assert (fklass->valuetype);
189                         memcpy (t, field->data, mono_class_value_size (fklass, NULL));
190                         continue;
191                 }
192                 if (!(field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT))
193                         continue;
194                 cindex = mono_metadata_get_constant_index (class->image, MONO_TOKEN_FIELD_DEF | (i + 1));
195                 if (!cindex) {
196                         g_warning ("constant for field %s not found", field->name);
197                         continue;
198                 }
199                 mono_metadata_decode_row (&class->image->tables [MONO_TABLE_CONSTANT], cindex - 1, cols, MONO_CONSTANT_SIZE);
200                 p = mono_metadata_blob_heap (class->image, cols [MONO_CONSTANT_VALUE]);
201                 len = mono_metadata_decode_blob_size (p, &p);
202                 t = (char*)vt->data + field->offset;
203                 /* should we check that the type matches? */
204                 switch (cols [MONO_CONSTANT_TYPE]) {
205                 case MONO_TYPE_BOOLEAN:
206                 case MONO_TYPE_U1:
207                 case MONO_TYPE_I1:
208                         *t = *p;
209                         break;
210                 case MONO_TYPE_CHAR:
211                 case MONO_TYPE_U2:
212                 case MONO_TYPE_I2: {
213                         guint16 *val = (guint16*)t;
214                         *val = read16 (p);
215                         break;
216                 }
217                 case MONO_TYPE_U4:
218                 case MONO_TYPE_I4: {
219                         guint32 *val = (guint32*)t;
220                         *val = read32 (p);
221                         break;
222                 }
223                 case MONO_TYPE_U8:
224                 case MONO_TYPE_I8: {
225                         guint64 *val = (guint64*)t;
226                         *val = read64 (p);
227                         break;
228                 }
229                 case MONO_TYPE_R4: {
230                         float *val = (float*)t;
231                         readr4 (p, val);
232                         break;
233                 }
234                 case MONO_TYPE_R8: {
235                         double *val = (double*)t;
236                         readr8 (p, val);
237                         break;
238                 }
239                 case MONO_TYPE_STRING: {
240                         /*gpointer *val = (gpointer*)t;
241                         *val = mono_string_new_utf16 (domain, (const guint16*)p, len/2);*/
242                         break;
243                 }
244                 case MONO_TYPE_CLASS:
245                         /* nothing to do, we malloc0 the data and the value can be 0 only */
246                         break;
247                 default:
248                         g_warning ("type 0x%02x should not be in constant table", cols [MONO_CONSTANT_TYPE]);
249                 }
250         }
251
252         vt->max_interface_id = class->max_interface_id;
253         
254         vt->interface_offsets = mono_mempool_alloc0 (domain->mp, 
255                 sizeof (gpointer) * (class->max_interface_id + 1));
256
257         /* initialize interface offsets */
258         for (k = class; k ; k = k->parent) {
259                 for (i = 0; i < k->interface_count; i++) {
260                         int slot;
261                         MonoClass *ic = k->interfaces [i];
262                         slot = class->interface_offsets [ic->interface_id];
263                         vt->interface_offsets [ic->interface_id] = &vt->vtable [slot];
264                 }
265         }
266
267         /* initialize vtable */
268         for (i = 0; i < class->vtable_size; ++i) {
269                 MonoMethod *cm;
270                
271                 if ((cm = class->vtable [i]))
272                         vt->vtable [i] = arch_create_jit_trampoline (cm);
273         }
274
275         mono_g_hash_table_insert (domain->class_vtable_hash, class, vt);
276         mono_domain_unlock (domain);
277
278         /* make sure the the parent is initialized */
279         if (class->parent)
280                 mono_class_vtable (domain, class->parent);
281
282         mono_runtime_class_init (class);
283         
284         return vt;
285 }
286
287 /**
288  * mono_class_proxy_vtable:
289  * @domain: the application domain
290  * @class: the class to proxy
291  *
292  * Creates a vtable for transparent proxies. It is basically
293  * a copy of the real vtable of @class, but all function pointers invoke
294  * the remoting functions, and vtable->klass points to the 
295  * transparent proxy class, and not to @class.
296  */
297 MonoVTable *
298 mono_class_proxy_vtable (MonoDomain *domain, MonoClass *class)
299 {
300         MonoVTable *vt, *pvt;
301         int i, vtsize;
302
303         if ((pvt = mono_g_hash_table_lookup (domain->proxy_vtable_hash, class)))
304                 return pvt;
305
306         vt = mono_class_vtable (domain, class);
307         vtsize = sizeof (MonoVTable) + class->vtable_size * sizeof (gpointer);
308
309         mono_stats.class_vtable_size += vtsize;
310
311         pvt = mono_mempool_alloc (domain->mp, vtsize);
312         memcpy (pvt, vt, vtsize);
313
314         pvt->klass = mono_defaults.transparent_proxy_class;
315
316         /* initialize vtable */
317         for (i = 0; i < class->vtable_size; ++i) {
318                 MonoMethod *cm;
319                
320                 if ((cm = class->vtable [i]))
321                         pvt->vtable [i] = arch_create_remoting_trampoline (cm);
322         }
323
324         mono_g_hash_table_insert (domain->proxy_vtable_hash, class, pvt);
325
326         return pvt;
327 }
328
329 static MonoInvokeFunc default_mono_runtime_invoke = NULL;
330
331 MonoObject*
332 mono_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
333 {
334         if (!default_mono_runtime_invoke) {
335                 g_error ("runtime invoke called on uninitialized runtime");
336                 return NULL;
337         }
338
339         return default_mono_runtime_invoke (method, obj, params, exc);
340 }
341
342 MonoMethod *
343 mono_get_delegate_invoke (MonoClass *klass)
344 {
345         MonoMethod *im;
346         int i;
347
348         im = NULL;
349
350         for (i = 0; i < klass->method.count; ++i) {
351                 if (klass->methods [i]->name[0] == 'I' && 
352                     !strcmp ("Invoke", klass->methods [i]->name)) {
353                         im = klass->methods [i];
354                 }
355         }
356
357         g_assert (im);
358
359         return im;
360 }
361
362 MonoObject*
363 mono_runtime_delegate_invoke (MonoObject *delegate, void **params, MonoObject **exc)
364 {
365         MonoMethod *im;
366
367         im = mono_get_delegate_invoke (delegate->vtable->klass);
368         g_assert (im);
369
370         return mono_runtime_invoke (im, delegate, params, exc);
371 }
372
373 static MonoArray* main_args;
374
375 MonoArray*
376 mono_runtime_get_main_args (void)
377 {
378         return main_args;
379 }
380
381 /*
382  * Execute a standard Main() method (argc/argv contains the
383  * executable name). This method also sets the command line argument value
384  * needed by System.Environment.
385  */
386 int
387 mono_runtime_run_main (MonoMethod *method, int argc, char* argv[],
388                        MonoObject **exc)
389 {
390         int i;
391         MonoArray *args = NULL;
392         MonoDomain *domain = mono_domain_get ();
393
394         main_args = (MonoArray*)mono_array_new (domain, mono_defaults.string_class, argc);
395         for (i = 0; i < argc; ++i) {
396                 MonoString *arg = mono_string_new (domain, argv [i]);
397                 mono_array_set (main_args, gpointer, i, arg);
398         }
399         argc--;
400         argv++;
401         if (method->signature->param_count) {
402                 args = (MonoArray*)mono_array_new (domain, mono_defaults.string_class, argc);
403                 for (i = 0; i < argc; ++i) {
404                         MonoString *arg = mono_string_new (domain, argv [i]);
405                         mono_array_set (args, gpointer, i, arg);
406                 }
407         }
408         
409         mono_assembly_set_main (method->klass->image->assembly);
410         
411         return mono_runtime_exec_main (method, args, exc);
412 }
413
414 /*
415  * We call this function when we dectect an unhandled exception. It invokes the
416  * UnhandledException event in AppDomain or print a warning to the console 
417  */
418 void
419 mono_unhandled_exception (MonoObject *exc)
420 {
421         MonoDomain *domain = mono_domain_get ();
422         MonoClassField *field;
423         MonoObject *delegate;
424         
425         field=mono_class_get_field_from_name(mono_defaults.appdomain_class, 
426                                              "UnhandledException");
427         g_assert (field);
428
429         delegate = *(MonoObject **)(((char *)domain->domain) + field->offset); 
430
431         if (!delegate) {
432                 mono_print_unhandled_exception (exc);
433         } else {
434                 MonoObject *e = NULL;
435                 gpointer pa [2];
436
437                 /* fixme: pass useful arguments */
438                 pa [0] = NULL;
439                 pa [1] = NULL;
440                 mono_runtime_delegate_invoke (delegate, pa, &e);
441                
442                 if (e)
443                         g_warning ("exception inside UnhandledException handler!");
444         }
445 }
446
447 /*
448  * Execute a standard Main() method (args doesn't contain the
449  * executable name).
450  */
451 int
452 mono_runtime_exec_main (MonoMethod *method, MonoArray *args, MonoObject **exc)
453 {
454         gpointer pa [1];
455         int rval;
456
457         pa [0] = args;
458
459         /* FIXME: check signature of method */
460         if (method->signature->ret->type == MONO_TYPE_I4) {
461                 MonoObject *res;
462                 res = mono_runtime_invoke (method, NULL, pa, exc);
463                 if (!exc || !*exc)
464                         rval = *(guint32 *)((char *)res + sizeof (MonoObject));
465                 else
466                         rval = -1;
467         } else {
468                 mono_runtime_invoke (method, NULL, pa, exc);
469                 if (!exc || !*exc)
470                         rval = 0;
471                 else
472                         rval = -1;
473         }
474
475         return rval;
476 }
477
478 void
479 mono_install_runtime_invoke (MonoInvokeFunc func)
480 {
481         default_mono_runtime_invoke = func;
482 }
483
484 MonoObject*
485 mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
486                            MonoObject **exc)
487 {
488         MonoMethodSignature *sig = method->signature;
489         gpointer *pa = NULL;
490         int i;
491                 
492         if (NULL != params) {
493                 pa = alloca (sizeof (gpointer) * mono_array_length (params));
494                 for (i = 0; i < mono_array_length (params); i++) {
495                         if (sig->params [i]->byref) {
496                                 /* nothing to do */
497                         }
498
499                         switch (sig->params [i]->type) {
500                         case MONO_TYPE_U1:
501                         case MONO_TYPE_I1:
502                         case MONO_TYPE_BOOLEAN:
503                         case MONO_TYPE_U2:
504                         case MONO_TYPE_I2:
505                         case MONO_TYPE_CHAR:
506                         case MONO_TYPE_U:
507                         case MONO_TYPE_I:
508                         case MONO_TYPE_U4:
509                         case MONO_TYPE_I4:
510                         case MONO_TYPE_U8:
511                         case MONO_TYPE_I8:
512                         case MONO_TYPE_VALUETYPE:
513                                 pa [i] = (char *)(((gpointer *)params->vector)[i]) + sizeof (MonoObject);
514                                 break;
515                         case MONO_TYPE_STRING:
516                         case MONO_TYPE_OBJECT:
517                         case MONO_TYPE_CLASS:
518                         case MONO_TYPE_SZARRAY:
519                                 pa [i] = (char *)(((gpointer *)params->vector)[i]);
520                                 break;
521                         default:
522                                 g_error ("type 0x%x not handled in ves_icall_InternalInvoke", sig->params [i]->type);
523                         }
524                 }
525         }
526
527         if (!strcmp (method->name, ".ctor") && method->klass != mono_defaults.string_class) {
528                 obj = mono_object_new (mono_domain_get (), method->klass);
529                 mono_runtime_invoke (method, obj, pa, exc);
530                 return obj;
531         } else
532                 return mono_runtime_invoke (method, obj, pa, exc);
533 }
534
535 /**
536  * mono_object_allocate:
537  * @size: number of bytes to allocate
538  *
539  * This is a very simplistic routine until we have our GC-aware
540  * memory allocator. 
541  *
542  * Returns: an allocated object of size @size, or NULL on failure.
543  */
544 void *
545 mono_object_allocate (size_t size)
546 {
547 #if HAVE_BOEHM_GC
548         /* if this is changed to GC_debug_malloc(), we need to change also metadata/gc.c */
549         void *o = GC_malloc (size);
550 #else
551         void *o = calloc (1, size);
552 #endif
553
554         return o;
555 }
556
557 /**
558  * mono_object_free:
559  *
560  * Frees the memory used by the object.  Debugging purposes
561  * only, as we will have our GC system.
562  */
563 void
564 mono_object_free (MonoObject *o)
565 {
566 #if HAVE_BOEHM_GC
567         g_error ("mono_object_free called with boehm gc.");
568 #else
569         MonoClass *c = o->vtable->klass;
570         
571         memset (o, 0, c->instance_size);
572         free (o);
573 #endif
574 }
575
576 /**
577  * mono_object_new:
578  * @klass: the class of the object that we want to create
579  *
580  * Returns: A newly created object whose definition is
581  * looked up using @klass
582  */
583 MonoObject *
584 mono_object_new (MonoDomain *domain, MonoClass *klass)
585 {
586         return mono_object_new_specific (mono_class_vtable (domain, klass));
587 }
588
589 /**
590  * mono_object_new_specific:
591  * @vtable: the vtable of the object that we want to create
592  *
593  * Returns: A newly created object with class and domain specified
594  * by @vtable
595  */
596 MonoObject *
597 mono_object_new_specific (MonoVTable *vtable)
598 {
599         MonoObject *o;
600
601         mono_stats.new_object_count++;
602
603         o = mono_object_allocate (vtable->klass->instance_size);
604         o->vtable = vtable;
605         if (vtable->klass->has_finalize)
606                 mono_object_register_finalizer (o);
607         
608         return o;
609 }
610
611 /**
612  * mono_object_new_from_token:
613  * @image: Context where the type_token is hosted
614  * @token: a token of the type that we want to create
615  *
616  * Returns: A newly created object whose definition is
617  * looked up using @token in the @image image
618  */
619 MonoObject *
620 mono_object_new_from_token  (MonoDomain *domain, MonoImage *image, guint32 token)
621 {
622         MonoClass *class;
623
624         class = mono_class_get (image, token);
625
626         return mono_object_new (domain, class);
627 }
628
629
630 /**
631  * mono_object_clone:
632  * @obj: the object to clone
633  *
634  * Returns: A newly created object who is a shallow copy of @obj
635  */
636 MonoObject *
637 mono_object_clone (MonoObject *obj)
638 {
639         MonoObject *o;
640         int size;
641
642         size = obj->vtable->klass->instance_size;
643         o = mono_object_allocate (size);
644
645         memcpy (o, obj, size);
646
647         if (obj->vtable->klass->has_finalize)
648                 mono_object_register_finalizer (o);
649         return o;
650 }
651
652 /**
653  * mono_array_clone:
654  * @array: the array to clone
655  *
656  * Returns: A newly created array who is a shallow copy of @array
657  */
658 MonoArray*
659 mono_array_clone (MonoArray *array)
660 {
661         MonoArray *o;
662         int size, i;
663         guint32 *sizes;
664         MonoClass *klass = array->obj.vtable->klass;
665
666         if (array->bounds == NULL) {
667                 size = mono_array_length (array);
668                 o = mono_array_new_full (((MonoObject *)array)->vtable->domain,
669                                          klass, &size, NULL);
670
671                 size *= mono_array_element_size (klass);
672                 memcpy (o, array, sizeof (MonoArray) + size);
673
674                 return o;
675         }
676         
677         sizes = alloca (klass->rank * sizeof(guint32) * 2);
678         size = mono_array_element_size (klass);
679         for (i = 0; i < klass->rank; ++i) {
680                 sizes [i] = array->bounds [i].length;
681                 size *= array->bounds [i].length;
682                 sizes [i + klass->rank] = array->bounds [i].lower_bound;
683         }
684         o = mono_array_new_full (((MonoObject *)array)->vtable->domain, 
685                                  klass, sizes, sizes + klass->rank);
686         memcpy (o, array, sizeof(MonoArray) + size);
687
688         return o;
689 }
690
691 /*
692  * mono_array_new_full:
693  * @domain: domain where the object is created
694  * @array_class: array class
695  * @lengths: lengths for each dimension in the array
696  * @lower_bounds: lower bounds for each dimension in the array (may be NULL)
697  *
698  * This routine creates a new array objects with the given dimensions,
699  * lower bounds and type.
700  */
701 MonoArray*
702 mono_array_new_full (MonoDomain *domain, MonoClass *array_class, 
703                      guint32 *lengths, guint32 *lower_bounds)
704 {
705         guint32 byte_len, len;
706         MonoObject *o;
707         MonoArray *array;
708         MonoArrayBounds *bounds;
709         int i;
710
711         if (!array_class->inited)
712                 mono_class_init (array_class);
713
714         byte_len = mono_array_element_size (array_class);
715         len = 1;
716
717         if (array_class->rank == 1 &&
718             (lower_bounds == NULL || lower_bounds [0] == 0)) {
719                 bounds = NULL;
720                 len = lengths [0];
721         } else {
722         #if HAVE_BOEHM_GC
723                 bounds = GC_malloc (sizeof (MonoArrayBounds) * array_class->rank);
724         #else
725                 bounds = g_malloc0 (sizeof (MonoArrayBounds) * array_class->rank);
726         #endif
727                 for (i = 0; i < array_class->rank; ++i) {
728                         bounds [i].length = lengths [i];
729                         len *= lengths [i];
730                 }
731
732                 if (lower_bounds)
733                         for (i = 0; i < array_class->rank; ++i)
734                                 bounds [i].lower_bound = lower_bounds [i];
735         }
736
737         byte_len *= len;
738         /* 
739          * Following three lines almost taken from mono_object_new ():
740          * they need to be kept in sync.
741          */
742         o = mono_object_allocate (sizeof (MonoArray) + byte_len);
743         if (!o)
744                 G_BREAKPOINT ();
745         o->vtable = mono_class_vtable (domain, array_class);
746
747         array = (MonoArray*)o;
748
749         array->bounds = bounds;
750         array->max_length = len;
751
752         return array;
753 }
754
755 /*
756  * mono_array_new:
757  * @domain: domain where the object is created
758  * @eclass: element class
759  * @n: number of array elements
760  *
761  * This routine creates a new szarray with @n elements of type @eclass.
762  */
763 MonoArray *
764 mono_array_new (MonoDomain *domain, MonoClass *eclass, guint32 n)
765 {
766         MonoClass *ac;
767
768         ac = mono_array_class_get (&eclass->byval_arg, 1);
769         g_assert (ac != NULL);
770
771         return mono_array_new_specific (mono_class_vtable (domain, ac), n);
772 }
773
774 /*
775  * mono_array_new_specific:
776  * @vtable: a vtable in the appropriate domain for an initialized class
777  * @n: number of array elements
778  *
779  * This routine is a fast alternative to mono_array_new() for code which
780  * can be sure about the domain it operates in.
781  */
782 MonoArray *
783 mono_array_new_specific (MonoVTable *vtable, guint32 n)
784 {
785         MonoObject *o;
786         MonoArray *ao;
787         gsize byte_len;
788
789         byte_len = n * mono_array_element_size (vtable->klass);
790         o = mono_object_allocate (sizeof (MonoArray) + byte_len);
791         if (!o)
792                 G_BREAKPOINT ();
793         o->vtable = vtable;
794
795         ao = (MonoArray *)o;
796         ao->bounds = NULL;
797         ao->max_length = n;
798
799         return ao;
800 }
801
802 /**
803  * mono_string_new_utf16:
804  * @text: a pointer to an utf16 string
805  * @len: the length of the string
806  *
807  * Returns: A newly created string object which contains @text.
808  */
809 MonoString *
810 mono_string_new_utf16 (MonoDomain *domain, const guint16 *text, gint32 len)
811 {
812         MonoString *s;
813         
814         s = mono_string_new_size (domain, len);
815         g_assert (s != NULL);
816
817         memcpy (mono_string_chars (s), text, len * 2);
818
819         return s;
820 }
821
822 /**
823  * mono_string_new_size:
824  * @text: a pointer to an utf16 string
825  * @len: the length of the string
826  *
827  * Returns: A newly created string object of @len
828  */
829 MonoString *
830 mono_string_new_size (MonoDomain *domain, gint32 len)
831 {
832         MonoString *s;
833
834         /* 
835          * enable to get a good speedup: we still need to figure out
836          * how the sync structure is freed.
837          */
838 #if 0
839         s = GC_malloc_atomic (sizeof (MonoString) + ((len + 1) * 2));
840         s->object.synchronisation = 0;
841         mono_string_chars (s) [len] = 0;
842 #else
843         s = (MonoString*)mono_object_allocate (sizeof (MonoString) + ((len + 1) * 2));
844 #endif
845         if (!s)
846                 G_BREAKPOINT ();
847
848         s->object.vtable = mono_class_vtable (domain, mono_defaults.string_class);
849         s->length = len;
850
851         return s;
852 }
853
854 /*
855  * mono_string_new_len:
856  * @text: a pointer to an utf8 string
857  * @length: number of bytes in @text to consider
858  *
859  * Returns: A newly created string object which contains @text.
860  */
861 MonoString*
862 mono_string_new_len (MonoDomain *domain, const char *text, guint length)
863 {
864         GError *error = NULL;
865         MonoString *o = NULL;
866         guint16 *ut;
867         glong items_written;
868
869         
870         ut = g_utf8_to_utf16 (text, length, NULL, &items_written, &error);
871
872         if (!error)
873                 o = mono_string_new_utf16 (domain, ut, items_written);
874         else 
875                 g_error_free (error);
876
877         g_free (ut);
878
879         return o;
880 }
881
882 /**
883  * mono_string_new:
884  * @text: a pointer to an utf8 string
885  *
886  * Returns: A newly created string object which contains @text.
887  */
888 MonoString*
889 mono_string_new (MonoDomain *domain, const char *text)
890 {
891         GError *error = NULL;
892         MonoString *o = NULL;
893         guint16 *ut;
894         glong items_written;
895         int l;
896
897         l = strlen (text);
898         
899         ut = g_utf8_to_utf16 (text, l, NULL, &items_written, &error);
900
901         if (!error)
902                 o = mono_string_new_utf16 (domain, ut, items_written);
903         else 
904                 g_error_free (error);
905
906         g_free (ut);
907
908         return o;
909 }
910
911 /*
912  * mono_string_new_wrapper:
913  * @text: pointer to utf8 characters.
914  *
915  * Helper function to create a string object from @text in the current domain.
916  */
917 MonoString*
918 mono_string_new_wrapper (const char *text)
919 {
920         MonoDomain *domain = mono_domain_get ();
921
922         return mono_string_new (domain, text);
923 }
924
925 /**
926  * mono_value_box:
927  * @class: the class of the value
928  * @value: a pointer to the unboxed data
929  *
930  * Returns: A newly created object which contains @value.
931  */
932 MonoObject *
933 mono_value_box (MonoDomain *domain, MonoClass *class, gpointer value)
934 {
935         MonoObject *res;
936         int size;
937
938         g_assert (class->valuetype);
939
940         size = mono_class_instance_size (class);
941         res = mono_object_allocate (size);
942         res->vtable = mono_class_vtable (domain, class);
943
944         size = size - sizeof (MonoObject);
945
946 #if NO_UNALIGNED_ACCESS
947         memcpy ((char *)res + sizeof (MonoObject), value, size);
948 #else
949         switch (size) {
950         case 1:
951                 *((guint8 *) res + sizeof (MonoObject)) = *(guint8 *) value;
952                 break;
953         case 2:
954                 *(guint16 *)((guint8 *) res + sizeof (MonoObject)) = *(guint16 *) value;
955                 break;
956         case 4:
957                 *(guint32 *)((guint8 *) res + sizeof (MonoObject)) = *(guint32 *) value;
958                 break;
959         case 8:
960                 *(guint64 *)((guint8 *) res + sizeof (MonoObject)) = *(guint64 *) value;
961                 break;
962         default:
963                 memcpy ((char *)res + sizeof (MonoObject), value, size);
964         }
965 #endif
966         if (class->has_finalize)
967                 mono_object_register_finalizer (res);
968         return res;
969 }
970
971 /**
972  * mono_object_isinst:
973  * @obj: an object
974  * @klass: a pointer to a class 
975  *
976  * Returns: @obj if @obj is derived from @klass
977  */
978 MonoObject *
979 mono_object_isinst (MonoObject *obj, MonoClass *klass)
980 {
981         MonoVTable *vt;
982         MonoClass *oklass;
983
984         if (!obj)
985                 return NULL;
986
987         vt = obj->vtable;
988         oklass = vt->klass;
989
990         if (!klass->inited)
991                 mono_class_init (klass);
992
993         if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
994                 if ((klass->interface_id <= oklass->max_interface_id) &&
995                     vt->interface_offsets [klass->interface_id])
996                         return obj;
997         } else {
998                 if (oklass == mono_defaults.transparent_proxy_class) {
999                         /* fixme: add check for IRemotingTypeInfo */
1000                         oklass = ((MonoTransparentProxy *)obj)->klass;
1001                 }
1002                 if (klass->rank) {
1003                         if (oklass->rank == klass->rank && 
1004                             (oklass->element_class->baseval - klass->element_class->baseval) <= 
1005                             klass->element_class->diffval)
1006                                 return obj;
1007                         
1008                 } else if ((oklass->baseval - klass->baseval) <= klass->diffval)
1009                         return obj;
1010         }
1011
1012         return NULL;
1013 }
1014
1015 static MonoString*
1016 mono_string_is_interned_lookup (MonoString *str, int insert)
1017 {
1018         MonoGHashTable *ldstr_table;
1019         MonoString *res;
1020         MonoDomain *domain;
1021         char *ins = g_malloc (4 + str->length * 2);
1022         char *p;
1023         int bloblen;
1024         
1025         /* Encode the length */
1026         p = ins;
1027         mono_metadata_encode_value (2 * str->length, p, &p);
1028         bloblen = p - ins;
1029         p = ins;
1030         mono_metadata_encode_value (bloblen + 2 * str->length, p, &p);
1031         bloblen = (p - ins) + 2 * str->length;
1032         /*
1033          * ins is stored in the hash table as a key and needs to have the same
1034          * representation as in the metadata: we swap the character bytes on big
1035          * endian boxes.
1036          */
1037 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1038         {
1039                 int i;
1040                 char *p2 = mono_string_chars (str);
1041                 for (i = 0; i < str->length; ++i) {
1042                         *p++ = p2 [1];
1043                         *p++ = p2 [0];
1044                         p2 += 2;
1045                 }
1046         }
1047 #else
1048         memcpy (p, mono_string_chars (str), str->length * 2);
1049 #endif
1050         domain = ((MonoObject *)str)->vtable->domain;
1051         ldstr_table = domain->ldstr_table;
1052         mono_domain_lock (domain);
1053         if ((res = mono_g_hash_table_lookup (ldstr_table, ins))) {
1054                 mono_domain_unlock (domain);
1055                 g_free (ins);
1056                 return res;
1057         }
1058         if (insert) {
1059                 mono_g_hash_table_insert (ldstr_table, ins, str);
1060                 mono_domain_unlock (domain);
1061                 return str;
1062         }
1063         mono_domain_unlock (domain);
1064         g_free (ins);
1065         return NULL;
1066 }
1067
1068 MonoString*
1069 mono_string_is_interned (MonoString *o)
1070 {
1071         return mono_string_is_interned_lookup (o, FALSE);
1072 }
1073
1074 MonoString*
1075 mono_string_intern (MonoString *str)
1076 {
1077         return mono_string_is_interned_lookup (str, TRUE);
1078 }
1079
1080 /*
1081  * mono_ldstr:
1082  * @domain: the domain where the string will be used.
1083  * @image: a metadata context
1084  * @idx: index into the user string table.
1085  * 
1086  * Implementation for the ldstr opcode.
1087  */
1088 MonoString*
1089 mono_ldstr (MonoDomain *domain, MonoImage *image, guint32 idx)
1090 {
1091         const char *str, *sig;
1092         MonoString *o;
1093         size_t len2;
1094                 
1095         sig = str = mono_metadata_user_string (image, idx);
1096
1097         mono_domain_lock (domain);
1098         if ((o = mono_g_hash_table_lookup (domain->ldstr_table, sig))) {
1099                 mono_domain_unlock (domain);
1100                 return o;
1101         }
1102         
1103         len2 = mono_metadata_decode_blob_size (str, &str);
1104         len2 >>= 1;
1105
1106         o = mono_string_new_utf16 (domain, (guint16*)str, len2);
1107 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1108         {
1109                 int i;
1110                 guint16 *p2 = (guint16*)mono_string_chars (o);
1111                 for (i = 0; i < len2; ++i) {
1112                         *p2 = GUINT16_FROM_LE (*p2);
1113                         ++p2;
1114                 }
1115         }
1116 #endif
1117         mono_g_hash_table_insert (domain->ldstr_table, (gpointer)sig, o);
1118         mono_domain_unlock (domain);
1119
1120         return o;
1121 }
1122
1123 /*
1124  * mono_string_to_utf8:
1125  * @s: a System.String
1126  *
1127  * Return the UTF8 representation for @s.
1128  * the resulting buffer nedds to be freed with g_free().
1129  */
1130 char *
1131 mono_string_to_utf8 (MonoString *s)
1132 {
1133         char *as;
1134         GError *error = NULL;
1135
1136         if (s == NULL)
1137                 return NULL;
1138
1139         if (!s->length)
1140                 return g_strdup ("");
1141
1142         as = g_utf16_to_utf8 (mono_string_chars (s), s->length, NULL, NULL, &error);
1143         if (error)
1144                 g_warning (error->message);
1145
1146         return as;
1147 }
1148
1149 /*
1150  * mono_string_to_utf16:
1151  * @s: a MonoString
1152  *
1153  * Return an null-terminated array of the utf-16 chars
1154  * contained in @s. The result must be freed with g_free().
1155  * This is a temporary helper until our string implementation
1156  * is reworked to always include the null terminating char.
1157  */
1158 gunichar2 *
1159 mono_string_to_utf16 (MonoString *s)
1160 {
1161         char *as;
1162
1163         if (s == NULL)
1164                 return NULL;
1165
1166         as = g_malloc ((s->length * 2) + 2);
1167         as [(s->length * 2)] = '\0';
1168         as [(s->length * 2) + 1] = '\0';
1169
1170         if (!s->length) {
1171                 return (gunichar2 *)(as);
1172         }
1173         
1174         memcpy (as, mono_string_chars(s), s->length * 2);
1175         return (gunichar2 *)(as);
1176 }
1177
1178 static void
1179 default_ex_handler (MonoException *ex)
1180 {
1181         MonoObject *o = (MonoObject*)ex;
1182         g_error ("Exception %s.%s raised in C code", o->vtable->klass->name_space, o->vtable->klass->name);
1183 }
1184
1185 static MonoExceptionFunc ex_handler = default_ex_handler;
1186
1187 void
1188 mono_install_handler        (MonoExceptionFunc func)
1189 {
1190         ex_handler = func? func: default_ex_handler;
1191 }
1192
1193 /*
1194  * mono_raise_exception:
1195  * @ex: exception object
1196  *
1197  * Signal the runtime that the exception @ex has been raised in unmanaged code.
1198  */
1199 void
1200 mono_raise_exception (MonoException *ex) 
1201 {
1202         ex_handler (ex);
1203 }
1204
1205 MonoWaitHandle *
1206 mono_wait_handle_new (MonoDomain *domain, HANDLE handle)
1207 {
1208         MonoWaitHandle *res;
1209
1210         res = (MonoWaitHandle *)mono_object_new (domain, mono_defaults.waithandle_class);
1211
1212         res->handle = handle;
1213
1214         return res;
1215 }
1216
1217 MonoAsyncResult *
1218 mono_async_result_new (MonoDomain *domain, HANDLE handle, MonoObject *state, gpointer data)
1219 {
1220         MonoAsyncResult *res;
1221
1222         res = (MonoAsyncResult *)mono_object_new (domain, mono_defaults.asyncresult_class);
1223
1224         res->data = data;
1225         res->async_state = state;
1226         res->handle = (MonoObject *)mono_wait_handle_new (domain, handle);
1227         res->sync_completed = FALSE;
1228         res->completed = FALSE;
1229
1230         return res;
1231 }
1232
1233 void
1234 mono_message_init (MonoDomain *domain,
1235                    MonoMethodMessage *this, 
1236                    MonoReflectionMethod *method,
1237                    MonoArray *out_args)
1238 {
1239         MonoMethodSignature *sig = method->method->signature;
1240         MonoString *name;
1241         int i, j;
1242         char **names;
1243         guint8 arg_type;
1244
1245         this->method = method;
1246
1247         this->args = mono_array_new (domain, mono_defaults.object_class, sig->param_count);
1248         this->arg_types = mono_array_new (domain, mono_defaults.byte_class, sig->param_count);
1249
1250         names = g_new (char *, sig->param_count);
1251         mono_method_get_param_names (method->method, (const char **) names);
1252         this->names = mono_array_new (domain, mono_defaults.string_class, sig->param_count);
1253         
1254         for (i = 0; i < sig->param_count; i++) {
1255                  name = mono_string_new (domain, names [i]);
1256                  mono_array_set (this->names, gpointer, i, name);       
1257         }
1258
1259         g_free (names);
1260         
1261         for (i = 0, j = 0; i < sig->param_count; i++) {
1262
1263                 if (sig->params [i]->byref) {
1264                         if (out_args) {
1265                                 gpointer arg = mono_array_get (out_args, gpointer, j);
1266                                 mono_array_set (this->args, gpointer, i, arg);
1267                                 j++;
1268                         }
1269                         arg_type = 2;
1270                         if (sig->params [i]->attrs & PARAM_ATTRIBUTE_IN)
1271                                 arg_type |= 1;
1272                 } else {
1273                         arg_type = 1;
1274                 }
1275
1276                 mono_array_set (this->arg_types, guint8, i, arg_type);
1277         }
1278 }
1279
1280 /**
1281  * mono_remoting_invoke:
1282  * @real_proxy: pointer to a RealProxy object
1283  * @msg: The MonoMethodMessage to execute
1284  * @exc: used to store exceptions
1285  * @out_args: used to store output arguments
1286  *
1287  * This is used to call RealProxy::Invoke(). RealProxy::Invoke() returns an
1288  * IMessage interface and it is not trivial to extract results from there. So
1289  * we call an helper method PrivateInvoke instead of calling
1290  * RealProxy::Invoke() directly.
1291  *
1292  * Returns: the result object.
1293  */
1294 MonoObject *
1295 mono_remoting_invoke (MonoObject *real_proxy, MonoMethodMessage *msg, 
1296                       MonoObject **exc, MonoArray **out_args)
1297 {
1298         static MonoMethod *im = NULL;
1299         gpointer pa [4];
1300
1301         /*static MonoObject *(*invoke) (gpointer, gpointer, MonoObject **, MonoArray **) = NULL;*/
1302
1303         /* FIXME: make this domain dependent */
1304         if (!im) {
1305                 MonoClass *klass;
1306                 int i;
1307
1308                 klass = mono_defaults.real_proxy_class; 
1309                        
1310                 for (i = 0; i < klass->method.count; ++i) {
1311                         if (!strcmp ("PrivateInvoke", klass->methods [i]->name) &&
1312                             klass->methods [i]->signature->param_count == 4) {
1313                                 im = klass->methods [i];
1314                                 break;
1315                         }
1316                 }
1317         
1318                 g_assert (im);
1319         }
1320
1321         pa [0] = real_proxy;
1322         pa [1] = msg;
1323         pa [2] = exc;
1324         pa [3] = out_args;
1325
1326         return mono_runtime_invoke (im, NULL, pa, exc);
1327 }
1328
1329 MonoObject *
1330 mono_message_invoke (MonoObject *target, MonoMethodMessage *msg, 
1331                      MonoObject **exc, MonoArray **out_args) 
1332 {
1333         if (target && target->vtable->klass == mono_defaults.transparent_proxy_class) {
1334
1335                 return mono_remoting_invoke ((MonoObject *)((MonoTransparentProxy *)target)->rp, 
1336                                              msg, exc, out_args);
1337
1338         } else {
1339                 MonoDomain *domain = mono_domain_get (); 
1340                 MonoMethod *method = msg->method->method;
1341                 MonoMethodSignature *sig = method->signature;
1342                 int i, j, outarg_count = 0;
1343
1344                 for (i = 0; i < sig->param_count; i++) {
1345                         if (sig->params [i]->byref) 
1346                                 outarg_count++;
1347                 }
1348
1349                 *out_args = mono_array_new (domain, mono_defaults.object_class, outarg_count);
1350                 *exc = NULL;
1351
1352                 for (i = 0, j = 0; i < sig->param_count; i++) {
1353                         if (sig->params [i]->byref) {
1354                                 gpointer arg;
1355                                 arg = mono_array_get (msg->args, gpointer, i);
1356                                 mono_array_set (*out_args, gpointer, j, arg);
1357                                 j++;
1358                         }
1359                 }
1360
1361                 return mono_runtime_invoke_array (method, target, msg->args, exc);
1362         }
1363 }
1364
1365 void
1366 mono_print_unhandled_exception (MonoObject *exc)
1367 {
1368         char *message = g_strdup ("");
1369         char *trace = g_strdup ("");
1370         MonoString *str; 
1371
1372         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
1373                 if ((str = ((MonoException *)exc)->message))
1374                         message = mono_string_to_utf8 (str);
1375                 if ((str = ((MonoException *)exc)->stack_trace))
1376                         trace = mono_string_to_utf8 (str);
1377         }                               
1378
1379         g_warning ("unhandled exception %s.%s: \"%s\"", exc->vtable->klass->name_space, 
1380                    exc->vtable->klass->name, message);
1381         
1382         if (trace) {
1383                 g_printerr (trace);
1384                 g_printerr ("\n");
1385         }
1386
1387         g_free (message);
1388         g_free (trace);
1389 }
1390
1391 /**
1392  * mono_delegate_ctor:
1393  * @this: pointer to an uninitialized delegate object
1394  * @target: target object
1395  * @addr: pointer to native code
1396  *
1397  * This is used to initialize a delegate. We also insert the method_info if
1398  * we find the info with mono_jit_info_table_find().
1399  */
1400 void
1401 mono_delegate_ctor (MonoObject *this, MonoObject *target, gpointer addr)
1402 {
1403         MonoDomain *domain = mono_domain_get ();
1404         MonoDelegate *delegate = (MonoDelegate *)this;
1405         MonoMethod *method = NULL;
1406         MonoClass *class;
1407         MonoJitInfo *ji;
1408
1409         g_assert (this);
1410         g_assert (addr);
1411
1412         class = this->vtable->klass;
1413
1414         if ((ji = mono_jit_info_table_find (domain, addr))) {
1415                 method = ji->method;
1416                 delegate->method_info = mono_method_get_object (domain, method);
1417         }
1418
1419         if (target && target->vtable->klass == mono_defaults.transparent_proxy_class) {
1420                 g_assert (method);
1421                 delegate->method_ptr = arch_create_remoting_trampoline (method);
1422                 delegate->target = target;
1423         } else {
1424                 delegate->method_ptr = addr;
1425                 delegate->target = target;
1426         }
1427 }
1428
1429 /**
1430  * mono_method_call_message_new:
1431  *
1432  * Translates arguments pointers into a Message.
1433  */
1434 MonoMethodMessage *
1435 mono_method_call_message_new (MonoMethod *method, gpointer *params, MonoMethod *invoke, 
1436                               MonoDelegate **cb, MonoObject **state)
1437 {
1438         MonoDomain *domain = mono_domain_get ();
1439         MonoMethodSignature *sig = method->signature;
1440         MonoMethodMessage *msg;
1441         int i, count, type;
1442
1443         msg = (MonoMethodMessage *)mono_object_new (domain, mono_defaults.mono_method_message_class); 
1444         
1445         if (invoke) {
1446                 mono_message_init (domain, msg, mono_method_get_object (domain, invoke), NULL);
1447                 count =  sig->param_count - 2;
1448         } else {
1449                 mono_message_init (domain, msg, mono_method_get_object (domain, method), NULL);
1450                 count =  sig->param_count;
1451         }
1452
1453         for (i = 0; i < count; i++) {
1454                 gpointer vpos;
1455                 MonoClass *class;
1456                 MonoObject *arg;
1457
1458                 if (sig->params [i]->byref)
1459                         vpos = *((gpointer *)params [i]);
1460                 else 
1461                         vpos = params [i];
1462
1463                 type = sig->params [i]->type;
1464                 class = mono_class_from_mono_type (sig->params [i]);
1465
1466                 if (class->valuetype)
1467                         arg = mono_value_box (domain, class, vpos);
1468                 else 
1469                         arg = *((MonoObject **)vpos);
1470                       
1471                 mono_array_set (msg->args, gpointer, i, arg);
1472         }
1473
1474         if (invoke) {
1475                 *cb = *((MonoDelegate **)params [i]);
1476                 i++;
1477                 *state = *((MonoObject **)params [i]);
1478         }
1479
1480         return msg;
1481 }
1482
1483 /**
1484  * mono_method_return_message_restore:
1485  *
1486  * Restore results from message based processing back to arguments pointers
1487  */
1488 void
1489 mono_method_return_message_restore (MonoMethod *method, gpointer *params, MonoArray *out_args)
1490 {
1491         MonoMethodSignature *sig = method->signature;
1492         int i, j, type, size, align;
1493         
1494         for (i = 0, j = 0; i < sig->param_count; i++) {
1495                 size = mono_type_stack_size (sig->params [i], &align);
1496                 
1497                 if (sig->params [i]->byref) {
1498                         char *arg = mono_array_get (out_args, gpointer, j);
1499                         type = sig->params [i]->type;
1500                         
1501                         switch (type) {
1502                         case MONO_TYPE_VOID:
1503                                 g_assert_not_reached ();
1504                                 break;
1505                         case MONO_TYPE_U1:
1506                         case MONO_TYPE_I1:
1507                         case MONO_TYPE_BOOLEAN:
1508                         case MONO_TYPE_U2:
1509                         case MONO_TYPE_I2:
1510                         case MONO_TYPE_CHAR:
1511                         case MONO_TYPE_U4:
1512                         case MONO_TYPE_I4:
1513                         case MONO_TYPE_I8:
1514                         case MONO_TYPE_U8:
1515                         case MONO_TYPE_R4:
1516                         case MONO_TYPE_R8:
1517                         case MONO_TYPE_VALUETYPE: {
1518                                 memcpy (*((gpointer *)params [i]), arg + sizeof (MonoObject), size); 
1519                                 break;
1520                         }
1521                         case MONO_TYPE_STRING:
1522                         case MONO_TYPE_CLASS: 
1523                         case MONO_TYPE_ARRAY:
1524                         case MONO_TYPE_SZARRAY:
1525                                 *((MonoObject **)params [i]) = (MonoObject *)arg;
1526                                 break;
1527                         default:
1528                                 g_assert_not_reached ();
1529                         }
1530
1531                         j++;
1532                 }
1533         }
1534 }
1535
1536
1537
1538