comctl32/imagelist: Use proper color format for merged image lists.
[wine] / dlls / jscript / string.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include "jscript.h"
23 #include "regexp.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28
29 #define UINT32_MAX 0xffffffff
30
31 typedef struct {
32     jsdisp_t dispex;
33     jsstr_t *str;
34 } StringInstance;
35
36 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
37 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
38 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
39 static const WCHAR anchorW[] = {'a','n','c','h','o','r',0};
40 static const WCHAR bigW[] = {'b','i','g',0};
41 static const WCHAR blinkW[] = {'b','l','i','n','k',0};
42 static const WCHAR boldW[] = {'b','o','l','d',0};
43 static const WCHAR charAtW[] = {'c','h','a','r','A','t',0};
44 static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0};
45 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
46 static const WCHAR fixedW[] = {'f','i','x','e','d',0};
47 static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0};
48 static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0};
49 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0};
50 static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0};
51 static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
52 static const WCHAR linkW[] = {'l','i','n','k',0};
53 static const WCHAR matchW[] = {'m','a','t','c','h',0};
54 static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0};
55 static const WCHAR searchW[] = {'s','e','a','r','c','h',0};
56 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
57 static const WCHAR smallW[] = {'s','m','a','l','l',0};
58 static const WCHAR splitW[] = {'s','p','l','i','t',0};
59 static const WCHAR strikeW[] = {'s','t','r','i','k','e',0};
60 static const WCHAR subW[] = {'s','u','b',0};
61 static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0};
62 static const WCHAR substrW[] = {'s','u','b','s','t','r',0};
63 static const WCHAR supW[] = {'s','u','p',0};
64 static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
65 static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
66 static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
67 static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
68 static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
69 static const WCHAR fromCharCodeW[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
70
71 static inline StringInstance *string_from_vdisp(vdisp_t *vdisp)
72 {
73     return (StringInstance*)vdisp->u.jsdisp;
74 }
75
76 static inline StringInstance *string_this(vdisp_t *jsthis)
77 {
78     return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL;
79 }
80
81 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **val)
82 {
83     StringInstance *string;
84
85     if((string = string_this(jsthis))) {
86         *val = jsstr_addref(string->str);
87         return S_OK;
88     }
89
90     return to_string(ctx, jsval_disp(jsthis->u.disp), val);
91 }
92
93 static HRESULT String_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
94         jsval_t *r)
95 {
96     TRACE("%p\n", jsthis);
97
98     switch(flags) {
99     case DISPATCH_PROPERTYGET: {
100         StringInstance *string = string_from_vdisp(jsthis);
101
102         *r = jsval_number(jsstr_length(string->str));
103         break;
104     }
105     default:
106         FIXME("unimplemented flags %x\n", flags);
107         return E_NOTIMPL;
108     }
109
110     return S_OK;
111 }
112
113 static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r)
114 {
115     StringInstance *string;
116
117     if(!(string = string_this(jsthis))) {
118         WARN("this is not a string object\n");
119         return E_FAIL;
120     }
121
122     if(r)
123         *r = jsval_string(jsstr_addref(string->str));
124     return S_OK;
125 }
126
127 /* ECMA-262 3rd Edition    15.5.4.2 */
128 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
129         jsval_t *r)
130 {
131     TRACE("\n");
132
133     return stringobj_to_string(jsthis, r);
134 }
135
136 /* ECMA-262 3rd Edition    15.5.4.2 */
137 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
138         jsval_t *r)
139 {
140     TRACE("\n");
141
142     return stringobj_to_string(jsthis, r);
143 }
144
145 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t *r, const WCHAR *tagname)
146 {
147     unsigned tagname_len;
148     jsstr_t *str, *ret;
149     WCHAR *ptr;
150     HRESULT hres;
151
152     hres = get_string_val(ctx, jsthis, &str);
153     if(FAILED(hres))
154         return hres;
155
156     if(!r) {
157         jsstr_release(str);
158         return S_OK;
159     }
160
161     tagname_len = strlenW(tagname);
162
163     ret = jsstr_alloc_buf(jsstr_length(str) + 2*tagname_len + 5);
164     if(!ret) {
165         jsstr_release(str);
166         return E_OUTOFMEMORY;
167     }
168
169     ptr = ret->str;
170     *ptr++ = '<';
171     memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
172     ptr += tagname_len;
173     *ptr++ = '>';
174
175     ptr += jsstr_flush(str, ptr);
176     jsstr_release(str);
177
178     *ptr++ = '<';
179     *ptr++ = '/';
180     memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
181     ptr += tagname_len;
182     *ptr = '>';
183
184     *r = jsval_string(ret);
185     return S_OK;
186 }
187
188 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, unsigned argc, jsval_t *argv, jsval_t *r,
189         const WCHAR *tagname, const WCHAR *attrname)
190 {
191     jsstr_t *str, *attr_value = NULL;
192     HRESULT hres;
193
194     hres = get_string_val(ctx, jsthis, &str);
195     if(FAILED(hres))
196         return hres;
197
198     if(argc) {
199         hres = to_string(ctx, argv[0], &attr_value);
200         if(FAILED(hres)) {
201             jsstr_release(str);
202             return hres;
203         }
204     }else {
205         attr_value = jsstr_undefined();
206     }
207
208     if(r) {
209         unsigned attrname_len = strlenW(attrname);
210         unsigned tagname_len = strlenW(tagname);
211         jsstr_t *ret;
212
213         ret = jsstr_alloc_buf(2*tagname_len + attrname_len + jsstr_length(attr_value) + jsstr_length(str) + 9);
214         if(ret) {
215             WCHAR *ptr = ret->str;
216
217             *ptr++ = '<';
218             memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
219             ptr += tagname_len;
220             *ptr++ = ' ';
221             memcpy(ptr, attrname, attrname_len*sizeof(WCHAR));
222             ptr += attrname_len;
223             *ptr++ = '=';
224             *ptr++ = '"';
225             ptr += jsstr_flush(attr_value, ptr);
226             *ptr++ = '"';
227             *ptr++ = '>';
228             ptr += jsstr_flush(str, ptr);
229
230             *ptr++ = '<';
231             *ptr++ = '/';
232             memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
233             ptr += tagname_len;
234             *ptr = '>';
235
236             *r = jsval_string(ret);
237         }else {
238             hres = E_OUTOFMEMORY;
239         }
240     }
241
242     jsstr_release(attr_value);
243     jsstr_release(str);
244     return hres;
245 }
246
247 static HRESULT String_anchor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
248         jsval_t *r)
249 {
250     static const WCHAR fontW[] = {'A',0};
251     static const WCHAR colorW[] = {'N','A','M','E',0};
252
253     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
254 }
255
256 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
257         jsval_t *r)
258 {
259     static const WCHAR bigtagW[] = {'B','I','G',0};
260     return do_attributeless_tag_format(ctx, jsthis, r, bigtagW);
261 }
262
263 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
264         jsval_t *r)
265 {
266     static const WCHAR blinktagW[] = {'B','L','I','N','K',0};
267     return do_attributeless_tag_format(ctx, jsthis, r, blinktagW);
268 }
269
270 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
271         jsval_t *r)
272 {
273     static const WCHAR boldtagW[] = {'B',0};
274     return do_attributeless_tag_format(ctx, jsthis, r, boldtagW);
275 }
276
277 /* ECMA-262 3rd Edition    15.5.4.5 */
278 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
279         jsval_t *r)
280 {
281     jsstr_t *str, *ret;
282     INT pos = 0;
283     HRESULT hres;
284
285     TRACE("\n");
286
287     hres = get_string_val(ctx, jsthis, &str);
288     if(FAILED(hres))
289         return hres;
290
291     if(argc) {
292         double d;
293
294         hres = to_integer(ctx, argv[0], &d);
295         if(FAILED(hres)) {
296             jsstr_release(str);
297             return hres;
298         }
299         pos = is_int32(d) ? d : -1;
300     }
301
302     if(!r) {
303         jsstr_release(str);
304         return S_OK;
305     }
306
307     if(0 <= pos && pos < jsstr_length(str)) {
308         ret = jsstr_substr(str, pos, 1);
309         if(!ret)
310             return E_OUTOFMEMORY;
311     }else {
312         ret = jsstr_empty();
313     }
314
315     *r = jsval_string(ret);
316     return S_OK;
317 }
318
319 /* ECMA-262 3rd Edition    15.5.4.5 */
320 static HRESULT String_charCodeAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
321         jsval_t *r)
322 {
323     jsstr_t *str;
324     DWORD idx = 0;
325     HRESULT hres;
326
327     TRACE("\n");
328
329     hres = get_string_val(ctx, jsthis, &str);
330     if(FAILED(hres))
331         return hres;
332
333     if(argc > 0) {
334         double d;
335
336         hres = to_integer(ctx, argv[0], &d);
337         if(FAILED(hres)) {
338             jsstr_release(str);
339             return hres;
340         }
341
342         if(!is_int32(d) || d < 0 || d >= jsstr_length(str)) {
343             jsstr_release(str);
344             if(r)
345                 *r = jsval_number(NAN);
346             return S_OK;
347         }
348
349         idx = d;
350     }
351
352     if(r)
353         *r = jsval_number(str->str[idx]);
354
355     jsstr_release(str);
356     return S_OK;
357 }
358
359 /* ECMA-262 3rd Edition    15.5.4.6 */
360 static HRESULT String_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
361         jsval_t *r)
362 {
363     unsigned len = 0, i, str_cnt;
364     jsstr_t **strs, *ret = NULL;
365     WCHAR *ptr;
366     HRESULT hres;
367
368     TRACE("\n");
369
370     str_cnt = argc+1;
371     strs = heap_alloc_zero(str_cnt * sizeof(*strs));
372     if(!strs)
373         return E_OUTOFMEMORY;
374
375     hres = to_string(ctx, jsval_disp(jsthis->u.disp), strs);
376     if(SUCCEEDED(hres)) {
377         for(i=0; i < argc; i++) {
378             hres = to_string(ctx, argv[i], strs+i+1);
379             if(FAILED(hres))
380                 break;
381         }
382     }
383
384     if(SUCCEEDED(hres)) {
385         for(i=0; i < str_cnt; i++) {
386             len += jsstr_length(strs[i]);
387             if(len > JSSTR_MAX_LENGTH) {
388                 hres = E_OUTOFMEMORY;
389                 break;
390             }
391         }
392
393         ret = jsstr_alloc_buf(len);
394         if(ret) {
395             ptr = ret->str;
396             for(i=0; i < str_cnt; i++)
397                 ptr += jsstr_flush(strs[i], ptr);
398         }else {
399             hres = E_OUTOFMEMORY;
400         }
401     }
402
403     for(i=0; i < str_cnt; i++)
404         jsstr_release(strs[i]);
405     heap_free(strs);
406
407     if(FAILED(hres))
408         return hres;
409
410     if(r)
411         *r = jsval_string(ret);
412     else
413         jsstr_release(ret);
414     return S_OK;
415 }
416
417 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
418         jsval_t *r)
419 {
420     static const WCHAR fixedtagW[] = {'T','T',0};
421     return do_attributeless_tag_format(ctx, jsthis, r, fixedtagW);
422 }
423
424 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
425         jsval_t *r)
426 {
427     static const WCHAR fontW[] = {'F','O','N','T',0};
428     static const WCHAR colorW[] = {'C','O','L','O','R',0};
429
430     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
431 }
432
433 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
434         jsval_t *r)
435 {
436     static const WCHAR fontW[] = {'F','O','N','T',0};
437     static const WCHAR colorW[] = {'S','I','Z','E',0};
438
439     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
440 }
441
442 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
443         jsval_t *r)
444 {
445     jsstr_t *search_str, *str;
446     int length, pos = 0;
447     INT ret = -1;
448     HRESULT hres;
449
450     TRACE("\n");
451
452     hres = get_string_val(ctx, jsthis, &str);
453     if(FAILED(hres))
454         return hres;
455
456     length = jsstr_length(str);
457     if(!argc) {
458         if(r)
459             *r = jsval_number(-1);
460         jsstr_release(str);
461         return S_OK;
462     }
463
464     hres = to_string(ctx, argv[0], &search_str);
465     if(FAILED(hres)) {
466         jsstr_release(str);
467         return hres;
468     }
469
470     if(argc >= 2) {
471         double d;
472
473         hres = to_integer(ctx, argv[1], &d);
474         if(SUCCEEDED(hres) && d > 0.0)
475             pos = is_int32(d) ? min(length, d) : length;
476     }
477
478     if(SUCCEEDED(hres)) {
479         const WCHAR *ptr;
480
481         ptr = strstrW(str->str+pos, search_str->str);
482         if(ptr)
483             ret = ptr - str->str;
484         else
485             ret = -1;
486     }
487
488     jsstr_release(search_str);
489     jsstr_release(str);
490     if(FAILED(hres))
491         return hres;
492
493     if(r)
494         *r = jsval_number(ret);
495     return S_OK;
496 }
497
498 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
499         jsval_t *r)
500 {
501     static const WCHAR italicstagW[] = {'I',0};
502     return do_attributeless_tag_format(ctx, jsthis, r, italicstagW);
503 }
504
505 /* ECMA-262 3rd Edition    15.5.4.8 */
506 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
507         jsval_t *r)
508 {
509     unsigned pos = 0, search_len, length;
510     jsstr_t *search_str, *str;
511     INT ret = -1;
512     HRESULT hres;
513
514     TRACE("\n");
515
516     hres = get_string_val(ctx, jsthis, &str);
517     if(FAILED(hres))
518         return hres;
519
520     if(!argc) {
521         if(r)
522             *r = jsval_number(-1);
523         jsstr_release(str);
524         return S_OK;
525     }
526
527     hres = to_string(ctx, argv[0], &search_str);
528     if(FAILED(hres)) {
529         jsstr_release(str);
530         return hres;
531     }
532
533     search_len = jsstr_length(search_str);
534     length = jsstr_length(str);
535
536     if(argc >= 2) {
537         double d;
538
539         hres = to_integer(ctx, argv[1], &d);
540         if(SUCCEEDED(hres) && d > 0)
541             pos = is_int32(d) ? min(length, d) : length;
542     }else {
543         pos = length;
544     }
545
546     if(SUCCEEDED(hres) && length >= search_len) {
547         const WCHAR *ptr;
548
549         for(ptr = str->str+min(pos, length-search_len); ptr >= str->str; ptr--) {
550             if(!memcmp(ptr, search_str->str, search_len*sizeof(WCHAR))) {
551                 ret = ptr-str->str;
552                 break;
553             }
554         }
555     }
556
557     jsstr_release(search_str);
558     jsstr_release(str);
559     if(FAILED(hres))
560         return hres;
561
562     if(r)
563         *r = jsval_number(ret);
564     return S_OK;
565 }
566
567 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
568         jsval_t *r)
569 {
570     static const WCHAR fontW[] = {'A',0};
571     static const WCHAR colorW[] = {'H','R','E','F',0};
572
573     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
574 }
575
576 /* ECMA-262 3rd Edition    15.5.4.10 */
577 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
578         jsval_t *r)
579 {
580     jsdisp_t *regexp = NULL;
581     jsstr_t *str;
582     HRESULT hres;
583
584     TRACE("\n");
585
586     if(!argc) {
587         if(r)
588             *r = jsval_null();
589         return S_OK;
590     }
591
592     if(is_object_instance(argv[0])) {
593         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
594         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
595             jsdisp_release(regexp);
596             regexp = NULL;
597         }
598     }
599
600     if(!regexp) {
601         jsstr_t *match_str;
602
603         hres = to_string(ctx, argv[0], &match_str);
604         if(FAILED(hres))
605             return hres;
606
607         hres = create_regexp(ctx, match_str, 0, &regexp);
608         jsstr_release(match_str);
609         if(FAILED(hres))
610             return hres;
611     }
612
613     hres = get_string_val(ctx, jsthis, &str);
614     if(SUCCEEDED(hres))
615         hres = regexp_string_match(ctx, regexp, str, r);
616
617     jsdisp_release(regexp);
618     jsstr_release(str);
619     return hres;
620 }
621
622 typedef struct {
623     WCHAR *buf;
624     DWORD size;
625     DWORD len;
626 } strbuf_t;
627
628 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
629 {
630     if(!len)
631         return S_OK;
632
633     if(len + buf->len > buf->size) {
634         WCHAR *new_buf;
635         DWORD new_size;
636
637         new_size = buf->size ? buf->size<<1 : 16;
638         if(new_size < buf->len+len)
639             new_size = buf->len+len;
640         if(buf->buf)
641             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
642         else
643             new_buf = heap_alloc(new_size*sizeof(WCHAR));
644         if(!new_buf)
645             return E_OUTOFMEMORY;
646
647         buf->buf = new_buf;
648         buf->size = new_size;
649     }
650
651     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
652     buf->len += len;
653     return S_OK;
654 }
655
656 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
657         jsstr_t *str, match_state_t *match, jsstr_t **ret)
658 {
659     jsval_t *argv;
660     unsigned argc;
661     jsval_t val;
662     jsstr_t *tmp_str;
663     DWORD i;
664     HRESULT hres = S_OK;
665
666     argc = match->paren_count+3;
667     argv = heap_alloc_zero(sizeof(*argv)*argc);
668     if(!argv)
669         return E_OUTOFMEMORY;
670
671     tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
672     if(!tmp_str)
673         hres = E_OUTOFMEMORY;
674     argv[0] = jsval_string(tmp_str);
675
676     if(SUCCEEDED(hres)) {
677         for(i=0; i < match->paren_count; i++) {
678             if(match->parens[i].index != -1)
679                 tmp_str = jsstr_substr(str, match->parens[i].index, match->parens[i].length);
680             else
681                 tmp_str = jsstr_empty();
682             if(!tmp_str) {
683                hres = E_OUTOFMEMORY;
684                break;
685             }
686             argv[i+1] = jsval_string(tmp_str);
687         }
688     }
689
690     if(SUCCEEDED(hres)) {
691         argv[match->paren_count+1] = jsval_number(match->cp-str->str - match->match_len);
692         argv[match->paren_count+2] = jsval_string(str);
693     }
694
695     if(SUCCEEDED(hres))
696         hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val);
697
698     for(i=0; i <= match->paren_count; i++)
699         jsstr_release(get_string(argv[i]));
700     heap_free(argv);
701
702     if(FAILED(hres))
703         return hres;
704
705     hres = to_string(ctx, val, ret);
706     jsval_release(val);
707     return hres;
708 }
709
710 /* ECMA-262 3rd Edition    15.5.4.11 */
711 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
712         jsval_t *r)
713 {
714     DWORD rep_len=0;
715     jsstr_t *rep_str = NULL, *match_str = NULL, *str;
716     jsdisp_t *rep_func = NULL, *regexp = NULL;
717     match_state_t *match = NULL, last_match = {0};
718     strbuf_t ret = {NULL,0,0};
719     DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
720     HRESULT hres = S_OK;
721
722     TRACE("\n");
723
724     hres = get_string_val(ctx, jsthis, &str);
725     if(FAILED(hres))
726         return hres;
727
728     if(!argc) {
729         if(r)
730             *r = jsval_string(str);
731         else
732             jsstr_release(str);
733         return S_OK;
734     }
735
736     if(is_object_instance(argv[0])) {
737         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
738         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
739             jsdisp_release(regexp);
740             regexp = NULL;
741         }
742     }
743
744     if(!regexp) {
745         hres = to_string(ctx, argv[0], &match_str);
746         if(FAILED(hres)) {
747             jsstr_release(str);
748             return hres;
749         }
750     }
751
752     if(argc >= 2) {
753         if(is_object_instance(argv[1])) {
754             rep_func = iface_to_jsdisp((IUnknown*)get_object(argv[1]));
755             if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
756                 jsdisp_release(rep_func);
757                 rep_func = NULL;
758             }
759         }
760
761         if(!rep_func) {
762             hres = to_string(ctx, argv[1], &rep_str);
763             if(SUCCEEDED(hres))
764                 rep_len = jsstr_length(rep_str);
765         }
766     }
767
768     if(SUCCEEDED(hres)) {
769         const WCHAR *ecp = str->str;
770
771         while(1) {
772             if(regexp) {
773                 hres = regexp_match_next(ctx, regexp, re_flags, str, &match);
774                 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
775
776                 if(hres == S_FALSE) {
777                     hres = S_OK;
778                     break;
779                 }
780                 if(FAILED(hres))
781                     break;
782
783                 last_match.cp = match->cp;
784                 last_match.match_len = match->match_len;
785             }else {
786                 if(re_flags & REM_ALLOC_RESULT) {
787                     re_flags &= ~REM_ALLOC_RESULT;
788                     match = &last_match;
789                     match->cp = str->str;
790                 }
791
792                 match->cp = strstrW(match->cp, match_str->str);
793                 if(!match->cp)
794                     break;
795                 match->match_len = jsstr_length(match_str);
796                 match->cp += match->match_len;
797             }
798
799             hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
800             ecp = match->cp;
801             if(FAILED(hres))
802                 break;
803
804             if(rep_func) {
805                 jsstr_t *cstr;
806
807                 hres = rep_call(ctx, rep_func, str, match, &cstr);
808                 if(FAILED(hres))
809                     break;
810
811                 hres = strbuf_append(&ret, cstr->str, jsstr_length(cstr));
812                 jsstr_release(cstr);
813                 if(FAILED(hres))
814                     break;
815             }else if(rep_str && regexp) {
816                 const WCHAR *ptr = rep_str->str, *ptr2;
817
818                 while((ptr2 = strchrW(ptr, '$'))) {
819                     hres = strbuf_append(&ret, ptr, ptr2-ptr);
820                     if(FAILED(hres))
821                         break;
822
823                     switch(ptr2[1]) {
824                     case '$':
825                         hres = strbuf_append(&ret, ptr2, 1);
826                         ptr = ptr2+2;
827                         break;
828                     case '&':
829                         hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
830                         ptr = ptr2+2;
831                         break;
832                     case '`':
833                         hres = strbuf_append(&ret, str->str, match->cp-str->str-match->match_len);
834                         ptr = ptr2+2;
835                         break;
836                     case '\'':
837                         hres = strbuf_append(&ret, ecp, (str->str+jsstr_length(str))-ecp);
838                         ptr = ptr2+2;
839                         break;
840                     default: {
841                         DWORD idx;
842
843                         if(!isdigitW(ptr2[1])) {
844                             hres = strbuf_append(&ret, ptr2, 1);
845                             ptr = ptr2+1;
846                             break;
847                         }
848
849                         idx = ptr2[1] - '0';
850                         if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
851                             idx = idx*10 + (ptr[2]-'0');
852                             ptr = ptr2+3;
853                         }else if(idx && idx <= match->paren_count) {
854                             ptr = ptr2+2;
855                         }else {
856                             hres = strbuf_append(&ret, ptr2, 1);
857                             ptr = ptr2+1;
858                             break;
859                         }
860
861                         if(match->parens[idx-1].index != -1)
862                             hres = strbuf_append(&ret, str->str+match->parens[idx-1].index,
863                                     match->parens[idx-1].length);
864                     }
865                     }
866
867                     if(FAILED(hres))
868                         break;
869                 }
870
871                 if(SUCCEEDED(hres))
872                     hres = strbuf_append(&ret, ptr, (rep_str->str+rep_len)-ptr);
873                 if(FAILED(hres))
874                     break;
875             }else if(rep_str) {
876                 hres = strbuf_append(&ret, rep_str->str, rep_len);
877                 if(FAILED(hres))
878                     break;
879             }else {
880                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
881
882                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
883                 if(FAILED(hres))
884                     break;
885             }
886
887             if(!regexp)
888                 break;
889             else if(!match->match_len)
890                 match->cp++;
891         }
892
893         if(SUCCEEDED(hres))
894             hres = strbuf_append(&ret, ecp, str->str+jsstr_length(str)-ecp);
895     }
896
897     if(rep_func)
898         jsdisp_release(rep_func);
899     if(rep_str)
900         jsstr_release(rep_str);
901     if(match_str)
902         jsstr_release(match_str);
903     if(regexp)
904         heap_free(match);
905
906     if(SUCCEEDED(hres) && last_match.cp && regexp) {
907         jsstr_release(ctx->last_match);
908         ctx->last_match = jsstr_addref(str);
909         ctx->last_match_index = last_match.cp-str->str-last_match.match_len;
910         ctx->last_match_length = last_match.match_len;
911     }
912
913     if(regexp)
914         jsdisp_release(regexp);
915     jsstr_release(str);
916
917     if(SUCCEEDED(hres) && r) {
918         jsstr_t *ret_str;
919
920         ret_str = jsstr_alloc_len(ret.buf, ret.len);
921         if(!ret_str)
922             return E_OUTOFMEMORY;
923
924         TRACE("= %s\n", debugstr_jsstr(ret_str));
925         *r = jsval_string(ret_str);
926     }
927
928     heap_free(ret.buf);
929     return hres;
930 }
931
932 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
933         jsval_t *r)
934 {
935     jsdisp_t *regexp = NULL;
936     jsstr_t *str;
937     match_state_t match, *match_ptr = &match;
938     HRESULT hres;
939
940     TRACE("\n");
941
942     hres = get_string_val(ctx, jsthis, &str);
943     if(FAILED(hres))
944         return hres;
945
946     if(!argc) {
947         if(r)
948             *r = jsval_null();
949         jsstr_release(str);
950         return S_OK;
951     }
952
953     if(is_object_instance(argv[0])) {
954         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
955         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
956             jsdisp_release(regexp);
957             regexp = NULL;
958         }
959     }
960
961     if(!regexp) {
962         hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
963         if(FAILED(hres)) {
964             jsstr_release(str);
965             return hres;
966         }
967     }
968
969     match.cp = str->str;
970     hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, str, &match_ptr);
971     jsstr_release(str);
972     jsdisp_release(regexp);
973     if(FAILED(hres))
974         return hres;
975
976     if(r)
977         *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str->str : -1);
978     return S_OK;
979 }
980
981 /* ECMA-262 3rd Edition    15.5.4.13 */
982 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
983         jsval_t *r)
984 {
985     int start=0, end, length;
986     jsstr_t *str;
987     double d;
988     HRESULT hres;
989
990     TRACE("\n");
991
992     hres = get_string_val(ctx, jsthis, &str);
993     if(FAILED(hres))
994         return hres;
995
996     length = jsstr_length(str);
997     if(argc) {
998         hres = to_integer(ctx, argv[0], &d);
999         if(FAILED(hres)) {
1000             jsstr_release(str);
1001             return hres;
1002         }
1003
1004         if(is_int32(d)) {
1005             start = d;
1006             if(start < 0) {
1007                 start = length + start;
1008                 if(start < 0)
1009                     start = 0;
1010             }else if(start > length) {
1011                 start = length;
1012             }
1013         }else if(d > 0) {
1014             start = length;
1015         }
1016     }
1017
1018     if(argc >= 2) {
1019         hres = to_integer(ctx, argv[1], &d);
1020         if(FAILED(hres)) {
1021             jsstr_release(str);
1022             return hres;
1023         }
1024
1025         if(is_int32(d)) {
1026             end = d;
1027             if(end < 0) {
1028                 end = length + end;
1029                 if(end < 0)
1030                     end = 0;
1031             }else if(end > length) {
1032                 end = length;
1033             }
1034         }else {
1035             end = d < 0.0 ? 0 : length;
1036         }
1037     }else {
1038         end = length;
1039     }
1040
1041     if(end < start)
1042         end = start;
1043
1044     if(r) {
1045         jsstr_t *retstr = jsstr_substr(str, start, end-start);
1046         if(!retstr) {
1047             jsstr_release(str);
1048             return E_OUTOFMEMORY;
1049         }
1050
1051         *r = jsval_string(retstr);
1052     }
1053
1054     jsstr_release(str);
1055     return S_OK;
1056 }
1057
1058 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1059         jsval_t *r)
1060 {
1061     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1062     return do_attributeless_tag_format(ctx, jsthis, r, smalltagW);
1063 }
1064
1065 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1066         jsval_t *r)
1067 {
1068     match_state_t match_result, *match_ptr = &match_result;
1069     DWORD length, i, match_len = 0;
1070     const WCHAR *ptr, *ptr2;
1071     unsigned limit = UINT32_MAX;
1072     jsdisp_t *array, *regexp = NULL;
1073     jsstr_t *str, *match_str = NULL, *tmp_str;
1074     HRESULT hres;
1075
1076     TRACE("\n");
1077
1078     if(argc != 1 && argc != 2) {
1079         FIXME("unsupported argc %u\n", argc);
1080         return E_NOTIMPL;
1081     }
1082
1083     hres = get_string_val(ctx, jsthis, &str);
1084     if(FAILED(hres))
1085         return hres;
1086
1087     length = jsstr_length(str);
1088
1089     if(argc > 1 && !is_undefined(argv[1])) {
1090         hres = to_uint32(ctx, argv[1], &limit);
1091         if(FAILED(hres)) {
1092             jsstr_release(str);
1093             return hres;
1094         }
1095     }
1096
1097     if(is_object_instance(argv[0])) {
1098         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
1099         if(regexp) {
1100             if(!is_class(regexp, JSCLASS_REGEXP)) {
1101                 jsdisp_release(regexp);
1102                 regexp = NULL;
1103             }
1104         }
1105     }
1106
1107     if(!regexp) {
1108         hres = to_string(ctx, argv[0], &match_str);
1109         if(FAILED(hres)) {
1110             jsstr_release(str);
1111             return hres;
1112         }
1113
1114         match_len = jsstr_length(match_str);
1115         if(!match_len) {
1116             jsstr_release(match_str);
1117             match_str = NULL;
1118         }
1119     }
1120
1121     hres = create_array(ctx, 0, &array);
1122
1123     if(SUCCEEDED(hres)) {
1124         ptr = str->str;
1125         match_result.cp = str->str;
1126         for(i=0; i<limit; i++) {
1127             if(regexp) {
1128                 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, str, &match_ptr);
1129                 if(hres != S_OK)
1130                     break;
1131                 ptr2 = match_result.cp - match_result.match_len;
1132             }else if(match_str) {
1133                 ptr2 = strstrW(ptr, match_str->str);
1134                 if(!ptr2)
1135                     break;
1136             }else {
1137                 if(!*ptr)
1138                     break;
1139                 ptr2 = ptr+1;
1140             }
1141
1142             tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1143             if(!tmp_str) {
1144                 hres = E_OUTOFMEMORY;
1145                 break;
1146             }
1147
1148             hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1149             jsstr_release(tmp_str);
1150             if(FAILED(hres))
1151                 break;
1152
1153             if(regexp)
1154                 ptr = match_result.cp;
1155             else if(match_str)
1156                 ptr = ptr2 + match_len;
1157             else
1158                 ptr++;
1159         }
1160     }
1161
1162     if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1163         DWORD len = (str->str+length) - ptr;
1164
1165         if(len || match_str) {
1166             tmp_str = jsstr_alloc_len(ptr, len);
1167
1168             if(tmp_str) {
1169                 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1170                 jsstr_release(tmp_str);
1171             }else {
1172                 hres = E_OUTOFMEMORY;
1173             }
1174         }
1175     }
1176
1177     if(regexp)
1178         jsdisp_release(regexp);
1179     if(match_str)
1180         jsstr_release(match_str);
1181     jsstr_release(str);
1182
1183     if(SUCCEEDED(hres) && r)
1184         *r = jsval_obj(array);
1185     else
1186         jsdisp_release(array);
1187
1188     return hres;
1189 }
1190
1191 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1192         jsval_t *r)
1193 {
1194     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1195     return do_attributeless_tag_format(ctx, jsthis, r, striketagW);
1196 }
1197
1198 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1199         jsval_t *r)
1200 {
1201     static const WCHAR subtagW[] = {'S','U','B',0};
1202     return do_attributeless_tag_format(ctx, jsthis, r, subtagW);
1203 }
1204
1205 /* ECMA-262 3rd Edition    15.5.4.15 */
1206 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1207         jsval_t *r)
1208 {
1209     INT start=0, end, length;
1210     jsstr_t *str;
1211     double d;
1212     HRESULT hres;
1213
1214     TRACE("\n");
1215
1216     hres = get_string_val(ctx, jsthis, &str);
1217     if(FAILED(hres))
1218         return hres;
1219
1220     length = jsstr_length(str);
1221     if(argc >= 1) {
1222         hres = to_integer(ctx, argv[0], &d);
1223         if(FAILED(hres)) {
1224             jsstr_release(str);
1225             return hres;
1226         }
1227
1228         if(d >= 0)
1229             start = is_int32(d) ? min(length, d) : length;
1230     }
1231
1232     if(argc >= 2) {
1233         hres = to_integer(ctx, argv[1], &d);
1234         if(FAILED(hres)) {
1235             jsstr_release(str);
1236             return hres;
1237         }
1238
1239         if(d >= 0)
1240             end = is_int32(d) ? min(length, d) : length;
1241         else
1242             end = 0;
1243     }else {
1244         end = length;
1245     }
1246
1247     if(start > end) {
1248         INT tmp = start;
1249         start = end;
1250         end = tmp;
1251     }
1252
1253     if(r) {
1254         jsstr_t *ret = jsstr_substr(str, start, end-start);
1255         if(ret)
1256             *r = jsval_string(ret);
1257         else
1258             hres = E_OUTOFMEMORY;
1259     }
1260     jsstr_release(str);
1261     return hres;
1262 }
1263
1264 /* ECMA-262 3rd Edition    B.2.3 */
1265 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1266         jsval_t *r)
1267 {
1268     int start=0, len, length;
1269     jsstr_t *str;
1270     double d;
1271     HRESULT hres;
1272
1273     TRACE("\n");
1274
1275     hres = get_string_val(ctx, jsthis, &str);
1276     if(FAILED(hres))
1277         return hres;
1278
1279     length = jsstr_length(str);
1280     if(argc >= 1) {
1281         hres = to_integer(ctx, argv[0], &d);
1282         if(FAILED(hres)) {
1283             jsstr_release(str);
1284             return hres;
1285         }
1286
1287         if(d >= 0)
1288             start = is_int32(d) ? min(length, d) : length;
1289     }
1290
1291     if(argc >= 2) {
1292         hres = to_integer(ctx, argv[1], &d);
1293         if(FAILED(hres)) {
1294             jsstr_release(str);
1295             return hres;
1296         }
1297
1298         if(d >= 0.0)
1299             len = is_int32(d) ? min(length-start, d) : length-start;
1300         else
1301             len = 0;
1302     }else {
1303         len = length-start;
1304     }
1305
1306     hres = S_OK;
1307     if(r) {
1308         jsstr_t *ret = jsstr_substr(str, start, len);
1309         if(ret)
1310             *r = jsval_string(ret);
1311         else
1312             hres = E_OUTOFMEMORY;
1313     }
1314
1315     jsstr_release(str);
1316     return hres;
1317 }
1318
1319 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1320         jsval_t *r)
1321 {
1322     static const WCHAR suptagW[] = {'S','U','P',0};
1323     return do_attributeless_tag_format(ctx, jsthis, r, suptagW);
1324 }
1325
1326 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1327         jsval_t *r)
1328 {
1329     jsstr_t *str;
1330     HRESULT  hres;
1331
1332     TRACE("\n");
1333
1334     hres = get_string_val(ctx, jsthis, &str);
1335     if(FAILED(hres))
1336         return hres;
1337
1338     if(r) {
1339         jsstr_t *ret;
1340
1341         ret = jsstr_alloc_buf(jsstr_length(str));
1342         if(!ret) {
1343             jsstr_release(str);
1344             return E_OUTOFMEMORY;
1345         }
1346
1347         jsstr_flush(str, ret->str);
1348         strlwrW(ret->str);
1349         *r = jsval_string(ret);
1350     }
1351     jsstr_release(str);
1352     return S_OK;
1353 }
1354
1355 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1356         jsval_t *r)
1357 {
1358     jsstr_t *str;
1359     HRESULT hres;
1360
1361     TRACE("\n");
1362
1363     hres = get_string_val(ctx, jsthis, &str);
1364     if(FAILED(hres))
1365         return hres;
1366
1367     if(r) {
1368         jsstr_t *ret;
1369
1370         ret = jsstr_alloc_buf(jsstr_length(str));
1371         if(!ret) {
1372             jsstr_release(str);
1373             return E_OUTOFMEMORY;
1374         }
1375
1376         jsstr_flush(str, ret->str);
1377         struprW(ret->str);
1378         *r = jsval_string(ret);
1379     }
1380     jsstr_release(str);
1381     return S_OK;
1382 }
1383
1384 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1385         jsval_t *r)
1386 {
1387     FIXME("\n");
1388     return E_NOTIMPL;
1389 }
1390
1391 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1392         jsval_t *r)
1393 {
1394     FIXME("\n");
1395     return E_NOTIMPL;
1396 }
1397
1398 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1399         jsval_t *r)
1400 {
1401     FIXME("\n");
1402     return E_NOTIMPL;
1403 }
1404
1405 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1406         jsval_t *r)
1407 {
1408     StringInstance *This = string_from_vdisp(jsthis);
1409
1410     TRACE("\n");
1411
1412     switch(flags) {
1413     case INVOKE_FUNC:
1414         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
1415     case DISPATCH_PROPERTYGET: {
1416         *r = jsval_string(jsstr_addref(This->str));
1417         break;
1418     }
1419     default:
1420         FIXME("flags %x\n", flags);
1421         return E_NOTIMPL;
1422     }
1423
1424     return S_OK;
1425 }
1426
1427 static void String_destructor(jsdisp_t *dispex)
1428 {
1429     StringInstance *This = (StringInstance*)dispex;
1430
1431     jsstr_release(This->str);
1432     heap_free(This);
1433 }
1434
1435 static unsigned String_idx_length(jsdisp_t *jsdisp)
1436 {
1437     StringInstance *string = (StringInstance*)jsdisp;
1438
1439     /*
1440      * NOTE: For invoke version < 2, indexed array is not implemented at all.
1441      * Newer jscript.dll versions implement it on string type, not class,
1442      * which is not how it should work according to spec. IE9 implements it
1443      * properly, but it uses its own JavaScript engine inside MSHTML. We
1444      * implement it here, but in the way IE9 and spec work.
1445      */
1446     return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1447 }
1448
1449 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1450 {
1451     StringInstance *string = (StringInstance*)jsdisp;
1452     jsstr_t *ret;
1453
1454     ret = jsstr_substr(string->str, idx, 1);
1455     if(!ret)
1456         return E_OUTOFMEMORY;
1457
1458     TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1459
1460     *r = jsval_string(ret);
1461     return S_OK;
1462 }
1463
1464 static const builtin_prop_t String_props[] = {
1465     {anchorW,                String_anchor,                PROPF_METHOD|1},
1466     {bigW,                   String_big,                   PROPF_METHOD},
1467     {blinkW,                 String_blink,                 PROPF_METHOD},
1468     {boldW,                  String_bold,                  PROPF_METHOD},
1469     {charAtW,                String_charAt,                PROPF_METHOD|1},
1470     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1471     {concatW,                String_concat,                PROPF_METHOD|1},
1472     {fixedW,                 String_fixed,                 PROPF_METHOD},
1473     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1474     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1475     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1476     {italicsW,               String_italics,               PROPF_METHOD},
1477     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1478     {lengthW,                String_length,                0},
1479     {linkW,                  String_link,                  PROPF_METHOD|1},
1480     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1481     {matchW,                 String_match,                 PROPF_METHOD|1},
1482     {replaceW,               String_replace,               PROPF_METHOD|1},
1483     {searchW,                String_search,                PROPF_METHOD},
1484     {sliceW,                 String_slice,                 PROPF_METHOD},
1485     {smallW,                 String_small,                 PROPF_METHOD},
1486     {splitW,                 String_split,                 PROPF_METHOD|2},
1487     {strikeW,                String_strike,                PROPF_METHOD},
1488     {subW,                   String_sub,                   PROPF_METHOD},
1489     {substrW,                String_substr,                PROPF_METHOD|2},
1490     {substringW,             String_substring,             PROPF_METHOD|2},
1491     {supW,                   String_sup,                   PROPF_METHOD},
1492     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1493     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1494     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1495     {toStringW,              String_toString,              PROPF_METHOD},
1496     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1497     {valueOfW,               String_valueOf,               PROPF_METHOD}
1498 };
1499
1500 static const builtin_info_t String_info = {
1501     JSCLASS_STRING,
1502     {NULL, String_value, 0},
1503     sizeof(String_props)/sizeof(*String_props),
1504     String_props,
1505     String_destructor,
1506     NULL
1507 };
1508
1509 static const builtin_prop_t StringInst_props[] = {
1510     {lengthW,                String_length,                0}
1511 };
1512
1513 static const builtin_info_t StringInst_info = {
1514     JSCLASS_STRING,
1515     {NULL, String_value, 0},
1516     sizeof(StringInst_props)/sizeof(*StringInst_props),
1517     StringInst_props,
1518     String_destructor,
1519     NULL,
1520     String_idx_length,
1521     String_idx_get
1522 };
1523
1524 /* ECMA-262 3rd Edition    15.5.3.2 */
1525 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1526         unsigned argc, jsval_t *argv, jsval_t *r)
1527 {
1528     DWORD i, code;
1529     jsstr_t *ret;
1530     HRESULT hres;
1531
1532     TRACE("\n");
1533
1534     ret = jsstr_alloc_buf(argc);
1535     if(!ret)
1536         return E_OUTOFMEMORY;
1537
1538     for(i=0; i<argc; i++) {
1539         hres = to_uint32(ctx, argv[i], &code);
1540         if(FAILED(hres)) {
1541             jsstr_release(ret);
1542             return hres;
1543         }
1544
1545         ret->str[i] = code;
1546     }
1547
1548     if(r)
1549         *r = jsval_string(ret);
1550     else
1551         jsstr_release(ret);
1552     return S_OK;
1553 }
1554
1555 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1556         jsval_t *r)
1557 {
1558     HRESULT hres;
1559
1560     TRACE("\n");
1561
1562     switch(flags) {
1563     case INVOKE_FUNC: {
1564         jsstr_t *str;
1565
1566         if(argc) {
1567             hres = to_string(ctx, argv[0], &str);
1568             if(FAILED(hres))
1569                 return hres;
1570         }else {
1571             str = jsstr_empty();
1572         }
1573
1574         *r = jsval_string(str);
1575         break;
1576     }
1577     case DISPATCH_CONSTRUCT: {
1578         jsdisp_t *ret;
1579
1580         if(argc) {
1581             jsstr_t *str;
1582
1583             hres = to_string(ctx, argv[0], &str);
1584             if(FAILED(hres))
1585                 return hres;
1586
1587             hres = create_string(ctx, str, &ret);
1588             jsstr_release(str);
1589         }else {
1590             hres = create_string(ctx, jsstr_empty(), &ret);
1591         }
1592
1593         if(FAILED(hres))
1594             return hres;
1595
1596         *r = jsval_obj(ret);
1597         break;
1598     }
1599
1600     default:
1601         FIXME("unimplemented flags: %x\n", flags);
1602         return E_NOTIMPL;
1603     }
1604
1605     return S_OK;
1606 }
1607
1608 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1609 {
1610     StringInstance *string;
1611     HRESULT hres;
1612
1613     string = heap_alloc_zero(sizeof(StringInstance));
1614     if(!string)
1615         return E_OUTOFMEMORY;
1616
1617     if(object_prototype)
1618         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1619     else
1620         hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1621     if(FAILED(hres)) {
1622         heap_free(string);
1623         return hres;
1624     }
1625
1626     string->str = jsstr_addref(str);
1627     *ret = string;
1628     return S_OK;
1629 }
1630
1631 static const builtin_prop_t StringConstr_props[] = {
1632     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1633 };
1634
1635 static const builtin_info_t StringConstr_info = {
1636     JSCLASS_FUNCTION,
1637     {NULL, Function_value, 0},
1638     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1639     StringConstr_props,
1640     NULL,
1641     NULL
1642 };
1643
1644 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1645 {
1646     StringInstance *string;
1647     HRESULT hres;
1648
1649     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1650
1651     hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1652     if(FAILED(hres))
1653         return hres;
1654
1655     hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1656             PROPF_CONSTR|1, &string->dispex, ret);
1657
1658     jsdisp_release(&string->dispex);
1659     return hres;
1660 }
1661
1662 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1663 {
1664     StringInstance *string;
1665     HRESULT hres;
1666
1667     hres = string_alloc(ctx, NULL, str, &string);
1668     if(FAILED(hres))
1669         return hres;
1670
1671     *ret = &string->dispex;
1672     return S_OK;
1673
1674 }