* src/vm/string.c,
[cacao.git] / src / vm / string.c
1 /* src/vm/string.c - java.lang.String related functions
2
3    Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    $Id: string.c 8230 2007-07-25 08:23:10Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33
34 #include "vm/types.h"
35
36 #include "vm/global.h"
37
38 #include "mm/memory.h"
39
40 #include "native/jni.h"
41
42 #include "native/include/java_lang_String.h"
43
44 #include "threads/lock-common.h"
45
46 #include "vm/builtin.h"
47 #include "vm/exceptions.h"
48 #include "vm/stringlocal.h"
49 #include "vm/vm.h"
50
51 #include "vmcore/options.h"
52 #include "vmcore/primitive.h"
53 #include "vmcore/statistics.h"
54 #include "vmcore/utf8.h"
55
56
57 /* global variables ***********************************************************/
58
59 /* hashsize must be power of 2 */
60
61 #define HASHTABLE_STRING_SIZE    2048   /* initial size of javastring-hash    */
62
63 hashtable hashtable_string;             /* hashtable for javastrings          */
64
65 #if defined(ENABLE_THREADS)
66 static java_objectheader *lock_hashtable_string;
67 #endif
68
69
70 /* string_init *****************************************************************
71
72    Initialize the string hashtable lock.
73
74 *******************************************************************************/
75
76 bool string_init(void)
77 {
78         /* create string (javastring) hashtable */
79
80         hashtable_create(&hashtable_string, HASHTABLE_STRING_SIZE);
81
82 #if defined(ENABLE_THREADS)
83         /* create string hashtable lock object */
84
85         lock_hashtable_string = NEW(java_objectheader);
86
87         LOCK_INIT_OBJECT_LOCK(lock_hashtable_string);
88 #endif
89
90         /* everything's ok */
91
92         return true;
93 }
94
95
96 /* stringtable_update **********************************************************
97
98    Traverses the javastring hashtable and sets the vftbl-entries of
99    javastrings which were temporarily set to NULL, because
100    java.lang.Object was not yet loaded.
101
102 *******************************************************************************/
103  
104 void stringtable_update(void)
105 {
106         java_lang_String *js;   
107         java_chararray *a;
108         literalstring *s;       /* hashtable entry */
109         int i;
110
111         for (i = 0; i < hashtable_string.size; i++) {
112                 s = hashtable_string.ptr[i];
113                 if (s) {
114                         while (s) {
115                                 js = (java_lang_String *) s->string;
116                                
117                                 if ((js == NULL) || (js->value == NULL)) {
118                                         /* error in hashtable found */
119
120                                         vm_abort("stringtable_update: invalid literalstring in hashtable");
121                                 }
122
123                                 a = js->value;
124
125                                 if (!js->header.vftbl) 
126                                         /* vftbl of javastring is NULL */ 
127                                         js->header.vftbl = class_java_lang_String->vftbl;
128
129                                 if (!a->header.objheader.vftbl) 
130                                         /* vftbl of character-array is NULL */ 
131                                         a->header.objheader.vftbl =
132                                                 primitive_arrayclass_get_by_type(ARRAYTYPE_CHAR)->vftbl;
133
134                                 /* follow link in external hash chain */
135                                 s = s->hashlink;
136                         }       
137                 }               
138         }
139 }
140
141
142 /* javastring_new_from_utf_buffer **********************************************
143
144    Create a new object of type java/lang/String with the text from
145    the specified utf8 buffer.
146
147    IN:
148       buffer.......points to first char in the buffer
149           blength......number of bytes to read from the buffer
150
151    RETURN VALUE:
152       the java.lang.String object, or
153       NULL if an exception has been thrown
154
155 *******************************************************************************/
156
157 static java_objectheader *javastring_new_from_utf_buffer(const char *buffer,
158                                                                                                                  u4 blength)
159 {
160         const char *utf_ptr;            /* current utf character in utf string    */
161         u4 utflength;                   /* length of utf-string if uncompressed   */
162         java_objectheader *o;
163         java_lang_String  *s;           /* result-string                          */
164         java_chararray    *a;
165         u4 i;
166
167         assert(buffer);
168
169         utflength = utf_get_number_of_u2s_for_buffer(buffer,blength);
170
171         o = builtin_new(class_java_lang_String);
172         a = builtin_newarray_char(utflength);
173
174         /* javastring or character-array could not be created */
175
176         if ((o == NULL) || (a == NULL))
177                 return NULL;
178
179         /* decompress utf-string */
180
181         utf_ptr = buffer;
182
183         for (i = 0; i < utflength; i++)
184                 a->data[i] = utf_nextu2((char **) &utf_ptr);
185         
186         /* set fields of the javastring-object */
187
188         s = (java_lang_String *) o;
189
190         s->value  = a;
191         s->offset = 0;
192         s->count  = utflength;
193
194         return o;
195 }
196
197
198 /* javastring_safe_new_from_utf8 ***********************************************
199
200    Create a new object of type java/lang/String with the text from
201    the specified UTF-8 string. This function is safe for invalid UTF-8.
202    (Invalid characters will be replaced by U+fffd.)
203
204    IN:
205       text.........the UTF-8 string, zero-terminated.
206
207    RETURN VALUE:
208       the java.lang.String object, or
209       NULL if an exception has been thrown
210
211 *******************************************************************************/
212
213 java_objectheader *javastring_safe_new_from_utf8(const char *text)
214 {
215         java_objectheader *o;
216         java_chararray    *a;
217         java_lang_String  *s;
218         s4 nbytes;
219         s4 len;
220
221         assert(text);
222
223         /* Get number of bytes. We need this to completely emulate the messy */
224         /* behaviour of the RI. :(                                           */
225
226         nbytes = strlen(text);
227
228         /* calculate number of Java characters */
229
230         len = utf8_safe_number_of_u2s(text, nbytes);
231
232         /* allocate the String object and the char array */
233
234         o = builtin_new(class_java_lang_String);
235         a = builtin_newarray_char(len);
236
237         /* javastring or character-array could not be created? */
238
239         if ((o == NULL) || (a == NULL))
240                 return NULL;
241
242         /* decompress UTF-8 string */
243
244         utf8_safe_convert_to_u2s(text, nbytes, a->data);
245
246         /* set fields of the String object */
247
248         s = (java_lang_String *) o;
249
250         s->value  = a;
251         s->offset = 0;
252         s->count  = len;
253
254         return o;
255 }
256
257
258 /* javastring_new_from_utf_string **********************************************
259
260    Create a new object of type java/lang/String with the text from
261    the specified zero-terminated utf8 string.
262
263    IN:
264       buffer.......points to first char in the buffer
265           blength......number of bytes to read from the buffer
266
267    RETURN VALUE:
268       the java.lang.String object, or
269       NULL if an exception has been thrown
270
271 *******************************************************************************/
272
273 java_objectheader *javastring_new_from_utf_string(const char *utfstr)
274 {
275         assert(utfstr);
276
277         return javastring_new_from_utf_buffer(utfstr, strlen(utfstr));
278 }
279
280
281 /* javastring_new **************************************************************
282
283    creates a new object of type java/lang/String with the text of 
284    the specified utf8-string
285
286    return: pointer to the string or NULL if memory is exhausted.        
287
288 *******************************************************************************/
289
290 java_objectheader *javastring_new(utf *u)
291 {
292         char *utf_ptr;                  /* current utf character in utf string    */
293         u4 utflength;                   /* length of utf-string if uncompressed   */
294         java_objectheader *o;
295         java_chararray    *a;
296         java_lang_String  *s;
297         s4 i;
298
299         if (u == NULL) {
300                 exceptions_throw_nullpointerexception();
301                 return NULL;
302         }
303
304         utf_ptr = u->text;
305         utflength = utf_get_number_of_u2s(u);
306
307         o = builtin_new(class_java_lang_String);
308         a = builtin_newarray_char(utflength);
309
310         /* javastring or character-array could not be created */
311
312         if ((o == NULL) || (a == NULL))
313                 return NULL;
314
315         /* decompress utf-string */
316
317         for (i = 0; i < utflength; i++)
318                 a->data[i] = utf_nextu2(&utf_ptr);
319         
320         /* set fields of the javastring-object */
321
322         s = (java_lang_String *) o;
323
324         s->value  = a;
325         s->offset = 0;
326         s->count  = utflength;
327
328         return o;
329 }
330
331
332 /* javastring_new_slash_to_dot *************************************************
333
334    creates a new object of type java/lang/String with the text of 
335    the specified utf8-string with slashes changed to dots
336
337    return: pointer to the string or NULL if memory is exhausted.        
338
339 *******************************************************************************/
340
341 java_objectheader *javastring_new_slash_to_dot(utf *u)
342 {
343         char *utf_ptr;                  /* current utf character in utf string    */
344         u4 utflength;                   /* length of utf-string if uncompressed   */
345         java_objectheader *o;
346         java_chararray    *a;
347         java_lang_String  *s;
348         s4 i;
349         u2 ch;
350
351         if (u == NULL) {
352                 exceptions_throw_nullpointerexception();
353                 return NULL;
354         }
355
356         utf_ptr = u->text;
357         utflength = utf_get_number_of_u2s(u);
358
359         o = builtin_new(class_java_lang_String);
360         a = builtin_newarray_char(utflength);
361
362         /* javastring or character-array could not be created */
363         if ((o == NULL) || (a == NULL))
364                 return NULL;
365
366         /* decompress utf-string */
367
368         for (i = 0; i < utflength; i++) {
369                 ch = utf_nextu2(&utf_ptr);
370                 if (ch == '/')
371                         ch = '.';
372                 a->data[i] = ch;
373         }
374         
375         /* set fields of the javastring-object */
376
377         s = (java_lang_String *) o;
378
379         s->value  = a;
380         s->offset = 0;
381         s->count  = utflength;
382
383         return o;
384 }
385
386
387 /* javastring_new_from_ascii ***************************************************
388
389    creates a new java/lang/String object which contains the given ASCII
390    C-string converted to UTF-16.
391
392    IN:
393       text.........string of ASCII characters
394
395    RETURN VALUE:
396       the java.lang.String object, or 
397       NULL if an exception has been thrown.
398
399 *******************************************************************************/
400
401 java_objectheader *javastring_new_from_ascii(const char *text)
402 {
403         s4 i;
404         s4 len;                             /* length of the string               */
405         java_objectheader *o;
406         java_lang_String  *s;
407         java_chararray    *a;
408
409         if (text == NULL) {
410                 exceptions_throw_nullpointerexception();
411                 return NULL;
412         }
413
414         len = strlen(text);
415
416         o = builtin_new(class_java_lang_String);
417         a = builtin_newarray_char(len);
418
419         /* javastring or character-array could not be created */
420
421         if ((o == NULL) || (a == NULL))
422                 return NULL;
423
424         /* copy text */
425
426         for (i = 0; i < len; i++)
427                 a->data[i] = text[i];
428         
429         /* set fields of the javastring-object */
430
431         s = (java_lang_String *) o;
432
433         s->value  = a;
434         s->offset = 0;
435         s->count  = len;
436
437         return o;
438 }
439
440
441 /* javastring_tochar ***********************************************************
442
443    converts a Java string into a C string.
444         
445    return: pointer to C string
446         
447    Caution: calling method MUST release the allocated memory!
448         
449 *******************************************************************************/
450
451 char *javastring_tochar(java_objectheader *so) 
452 {
453         java_lang_String *s = (java_lang_String *) so;
454         java_chararray *a;
455         char *buf;
456         s4 i;
457         
458         if (!s)
459                 return "";
460
461         a = s->value;
462
463         if (!a)
464                 return "";
465
466         buf = MNEW(char, s->count + 1);
467
468         for (i = 0; i < s->count; i++)
469                 buf[i] = a->data[s->offset + i];
470
471         buf[i] = '\0';
472
473         return buf;
474 }
475
476
477 /* javastring_toutf ************************************************************
478
479    Make utf symbol from javastring.
480
481 *******************************************************************************/
482
483 utf *javastring_toutf(java_objectheader *string, bool isclassname)
484 {
485         java_lang_String *s;
486
487         s = (java_lang_String *) string;
488
489         if (s == NULL)
490                 return utf_null;
491
492         return utf_new_u2(s->value->data + s->offset, s->count, isclassname);
493 }
494
495
496 /* literalstring_u2 ************************************************************
497
498    Searches for the javastring with the specified u2-array in the
499    string hashtable, if there is no such string a new one is created.
500
501    If copymode is true a copy of the u2-array is made.
502
503 *******************************************************************************/
504
505 java_objectheader *literalstring_u2(java_chararray *a, u4 length, u4 offset,
506                                                                         bool copymode)
507 {
508     literalstring    *s;                /* hashtable element                  */
509     java_lang_String *js;               /* u2-array wrapped in javastring     */
510     java_chararray   *ca;               /* copy of u2-array                   */
511     u4                key;
512     u4                slot;
513     u2                i;
514
515         LOCK_MONITOR_ENTER(lock_hashtable_string);
516
517     /* find location in hashtable */
518
519     key  = unicode_hashkey(a->data + offset, length);
520     slot = key & (hashtable_string.size - 1);
521     s    = hashtable_string.ptr[slot];
522
523     while (s) {
524                 js = (java_lang_String *) s->string;
525
526                 if (length == js->count) {
527                         /* compare text */
528
529                         for (i = 0; i < length; i++)
530                                 if (a->data[offset + i] != js->value->data[i])
531                                         goto nomatch;
532
533                         /* string already in hashtable, free memory */
534
535                         if (!copymode)
536                                 mem_free(a, sizeof(java_chararray) + sizeof(u2) * (length - 1) + 10);
537
538                         LOCK_MONITOR_EXIT(lock_hashtable_string);
539
540                         return (java_objectheader *) js;
541                 }
542
543         nomatch:
544                 /* follow link in external hash chain */
545                 s = s->hashlink;
546     }
547
548     if (copymode) {
549                 /* create copy of u2-array for new javastring */
550                 u4 arraysize = sizeof(java_chararray) + sizeof(u2) * (length - 1) + 10;
551                 ca = mem_alloc(arraysize);
552 /*              memcpy(ca, a, arraysize); */
553                 memcpy(&(ca->header), &(a->header), sizeof(java_arrayheader));
554                 memcpy(&(ca->data), &(a->data) + offset, sizeof(u2) * (length - 1) + 10);
555
556     } else {
557                 ca = a;
558         }
559
560     /* location in hashtable found, complete arrayheader */
561
562     ca->header.objheader.vftbl =
563                 primitive_arrayclass_get_by_type(ARRAYTYPE_CHAR)->vftbl;
564     ca->header.size            = length;
565
566         assert(class_java_lang_String);
567         assert(class_java_lang_String->state & CLASS_LOADED);
568
569         /* create new javastring */
570
571         js = NEW(java_lang_String);
572
573 #if defined(ENABLE_STATISTICS)
574         if (opt_stat)
575                 size_string += sizeof(java_lang_String);
576 #endif
577
578 #if defined(ENABLE_THREADS)
579         lock_init_object_lock(&js->header);
580 #endif
581
582         js->header.vftbl = class_java_lang_String->vftbl;
583         js->value  = ca;
584         js->offset = 0;
585         js->count  = length;
586
587         /* create new literalstring */
588
589         s = NEW(literalstring);
590
591 #if defined(ENABLE_STATISTICS)
592         if (opt_stat)
593                 size_string += sizeof(literalstring);
594 #endif
595
596         s->hashlink = hashtable_string.ptr[slot];
597         s->string   = (java_objectheader *) js;
598         hashtable_string.ptr[slot] = s;
599
600         /* update number of hashtable entries */
601
602         hashtable_string.entries++;
603
604         /* reorganization of hashtable */       
605
606         if (hashtable_string.entries > (hashtable_string.size * 2)) {
607                 /* reorganization of hashtable, average length of the external
608                    chains is approx. 2 */
609
610                 u4                i;
611                 literalstring    *s;
612                 literalstring    *nexts;
613                 java_lang_String *tmpjs;
614                 hashtable         newhash;                       /* the new hashtable */
615       
616                 /* create new hashtable, double the size */
617
618                 hashtable_create(&newhash, hashtable_string.size * 2);
619                 newhash.entries = hashtable_string.entries;
620       
621                 /* transfer elements to new hashtable */
622
623                 for (i = 0; i < hashtable_string.size; i++) {
624                         s = hashtable_string.ptr[i];
625
626                         while (s) {
627                                 nexts = s->hashlink;
628                                 tmpjs = (java_lang_String *) s->string;
629                                 slot  = unicode_hashkey(tmpjs->value->data, tmpjs->count) & (newhash.size - 1);
630           
631                                 s->hashlink = newhash.ptr[slot];
632                                 newhash.ptr[slot] = s;
633         
634                                 /* follow link in external hash chain */
635                                 s = nexts;
636                         }
637                 }
638         
639                 /* dispose old table */
640
641                 MFREE(hashtable_string.ptr, void*, hashtable_string.size);
642                 hashtable_string = newhash;
643         }
644
645         LOCK_MONITOR_EXIT(lock_hashtable_string);
646
647         return (java_objectheader *) js;
648 }
649
650
651 /* literalstring_new ***********************************************************
652
653    Creates a new javastring with the text of the utf-symbol and inserts it into
654    the string hashtable.
655
656 *******************************************************************************/
657
658 java_objectheader *literalstring_new(utf *u)
659 {
660     char           *utf_ptr;         /* pointer to current unicode character  */
661                                          /* utf string                            */
662     u4              utflength;       /* length of utf-string if uncompressed  */
663     java_chararray *a;               /* u2-array constructed from utf string  */
664     u4              i;
665
666         utf_ptr = u->text;
667         utflength = utf_get_number_of_u2s(u);
668
669     /* allocate memory */ 
670     a = mem_alloc(sizeof(java_chararray) + sizeof(u2) * (utflength - 1) + 10);
671
672     /* convert utf-string to u2-array */
673     for (i = 0; i < utflength; i++)
674                 a->data[i] = utf_nextu2(&utf_ptr);
675
676     return literalstring_u2(a, utflength, 0, false);
677 }
678
679
680 /* literalstring_free **********************************************************
681
682    Removes a javastring from memory.
683
684 *******************************************************************************/
685
686 void literalstring_free(java_objectheader* string)
687 {
688         java_lang_String *s;
689         java_chararray *a;
690
691         s = (java_lang_String *) string;
692         a = s->value;
693
694         /* dispose memory of java.lang.String object */
695         FREE(s, java_lang_String);
696
697         /* dispose memory of java-characterarray */
698         FREE(a, sizeof(java_chararray) + sizeof(u2) * (a->header.size - 1)); /* +10 ?? */
699 }
700
701
702 /*
703  * These are local overrides for various environment variables in Emacs.
704  * Please do not remove this and leave it at the end of the file, where
705  * Emacs will automagically detect them.
706  * ---------------------------------------------------------------------
707  * Local variables:
708  * mode: c
709  * indent-tabs-mode: t
710  * c-basic-offset: 4
711  * tab-width: 4
712  * End:
713  * vim:noexpandtab:sw=4:ts=4:
714  */