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