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