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