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