jscript: Added support for limit argument in String.split.
[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 #define UINT32_MAX 0xffffffff
29
30 typedef struct {
31     jsdisp_t dispex;
32
33     WCHAR *str;
34     DWORD length;
35 } StringInstance;
36
37 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
38 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
39 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
40 static const WCHAR anchorW[] = {'a','n','c','h','o','r',0};
41 static const WCHAR bigW[] = {'b','i','g',0};
42 static const WCHAR blinkW[] = {'b','l','i','n','k',0};
43 static const WCHAR boldW[] = {'b','o','l','d',0};
44 static const WCHAR charAtW[] = {'c','h','a','r','A','t',0};
45 static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0};
46 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
47 static const WCHAR fixedW[] = {'f','i','x','e','d',0};
48 static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0};
49 static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0};
50 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0};
51 static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0};
52 static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
53 static const WCHAR linkW[] = {'l','i','n','k',0};
54 static const WCHAR matchW[] = {'m','a','t','c','h',0};
55 static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0};
56 static const WCHAR searchW[] = {'s','e','a','r','c','h',0};
57 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
58 static const WCHAR smallW[] = {'s','m','a','l','l',0};
59 static const WCHAR splitW[] = {'s','p','l','i','t',0};
60 static const WCHAR strikeW[] = {'s','t','r','i','k','e',0};
61 static const WCHAR subW[] = {'s','u','b',0};
62 static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0};
63 static const WCHAR substrW[] = {'s','u','b','s','t','r',0};
64 static const WCHAR supW[] = {'s','u','p',0};
65 static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
66 static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
67 static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
68 static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
69 static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
70 static const WCHAR fromCharCodeW[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
71
72 static inline StringInstance *string_from_vdisp(vdisp_t *vdisp)
73 {
74     return (StringInstance*)vdisp->u.jsdisp;
75 }
76
77 static inline StringInstance *string_this(vdisp_t *jsthis)
78 {
79     return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL;
80 }
81
82 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, const WCHAR **str, DWORD *len, BSTR *val_str)
83 {
84     StringInstance *string;
85     HRESULT hres;
86
87     if((string = string_this(jsthis))) {
88         *str = string->str;
89         *len = string->length;
90         *val_str = NULL;
91         return S_OK;
92     }
93
94     hres = to_string(ctx, jsval_disp(jsthis->u.disp), val_str);
95     if(FAILED(hres))
96         return hres;
97
98     *str = *val_str;
99     *len = SysStringLen(*val_str);
100     return S_OK;
101 }
102
103 static HRESULT String_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
104         jsval_t *r)
105 {
106     TRACE("%p\n", jsthis);
107
108     switch(flags) {
109     case DISPATCH_PROPERTYGET: {
110         StringInstance *string = string_from_vdisp(jsthis);
111
112         *r = jsval_number(string->length);
113         break;
114     }
115     default:
116         FIXME("unimplemented flags %x\n", flags);
117         return E_NOTIMPL;
118     }
119
120     return S_OK;
121 }
122
123 static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r)
124 {
125     StringInstance *string;
126
127     if(!(string = string_this(jsthis))) {
128         WARN("this is not a string object\n");
129         return E_FAIL;
130     }
131
132     if(r) {
133         BSTR str = SysAllocString(string->str);
134         if(!str)
135             return E_OUTOFMEMORY;
136
137         *r = jsval_string(str);
138     }
139     return S_OK;
140 }
141
142 /* ECMA-262 3rd Edition    15.5.4.2 */
143 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
144         jsval_t *r)
145 {
146     TRACE("\n");
147
148     return stringobj_to_string(jsthis, r);
149 }
150
151 /* ECMA-262 3rd Edition    15.5.4.2 */
152 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
153         jsval_t *r)
154 {
155     TRACE("\n");
156
157     return stringobj_to_string(jsthis, r);
158 }
159
160 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t *r, 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, &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         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), &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], &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)
249 {
250     static const WCHAR fontW[] = {'A',0};
251     static const WCHAR colorW[] = {'N','A','M','E',0};
252
253     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
254 }
255
256 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
257         jsval_t *r)
258 {
259     static const WCHAR bigtagW[] = {'B','I','G',0};
260     return do_attributeless_tag_format(ctx, jsthis, r, bigtagW);
261 }
262
263 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
264         jsval_t *r)
265 {
266     static const WCHAR blinktagW[] = {'B','L','I','N','K',0};
267     return do_attributeless_tag_format(ctx, jsthis, r, blinktagW);
268 }
269
270 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
271         jsval_t *r)
272 {
273     static const WCHAR boldtagW[] = {'B',0};
274     return do_attributeless_tag_format(ctx, jsthis, r, boldtagW);
275 }
276
277 /* ECMA-262 3rd Edition    15.5.4.5 */
278 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
279         jsval_t *r)
280 {
281     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, &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], &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)
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, &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], &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)
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), strs);
380     if(SUCCEEDED(hres)) {
381         for(i=0; i < argc; i++) {
382             hres = to_string(ctx, argv[i], 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)
417 {
418     static const WCHAR fixedtagW[] = {'T','T',0};
419     return do_attributeless_tag_format(ctx, jsthis, r, 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)
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, 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)
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, 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)
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, &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], &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], &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)
498 {
499     static const WCHAR italicstagW[] = {'I',0};
500     return do_attributeless_tag_format(ctx, jsthis, r, 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)
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, &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], &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], &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)
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, 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)
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], &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, &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);
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)
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);
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, 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)
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, &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], &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], &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);
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)
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, &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)
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, &str, &length, &val_str);
1010     if(FAILED(hres))
1011         return hres;
1012
1013     if(argc) {
1014         hres = to_integer(ctx, argv[0], &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], &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)
1076 {
1077     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1078     return do_attributeless_tag_format(ctx, jsthis, r, 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)
1083 {
1084     match_result_t *match_result = NULL;
1085     DWORD length, match_cnt, i, match_len = 0;
1086     const WCHAR *str, *ptr, *ptr2;
1087     unsigned limit = UINT32_MAX;
1088     BOOL use_regexp = FALSE;
1089     jsdisp_t *array;
1090     BSTR val_str, match_str = NULL, tmp_str;
1091     HRESULT hres;
1092
1093     TRACE("\n");
1094
1095     if(argc != 1 && argc != 2) {
1096         FIXME("unsupported argc %u\n", argc);
1097         return E_NOTIMPL;
1098     }
1099
1100     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1101     if(FAILED(hres))
1102         return hres;
1103
1104     if(argc > 1 && !is_undefined(argv[1])) {
1105         hres = to_uint32(ctx, argv[1], &limit);
1106         if(FAILED(hres)) {
1107             SysFreeString(val_str);
1108             return hres;
1109         }
1110     }
1111
1112     if(is_object_instance(argv[0])) {
1113         jsdisp_t *regexp;
1114
1115         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
1116         if(regexp) {
1117             if(is_class(regexp, JSCLASS_REGEXP)) {
1118                 use_regexp = TRUE;
1119                 hres = regexp_match(ctx, regexp, str, length, TRUE, &match_result, &match_cnt);
1120             }
1121             jsdisp_release(regexp);
1122             if(FAILED(hres)) {
1123                 SysFreeString(val_str);
1124                 return hres;
1125             }
1126         }
1127     }
1128
1129     if(!use_regexp) {
1130         hres = to_string(ctx, argv[0], &match_str);
1131         if(FAILED(hres)) {
1132             SysFreeString(val_str);
1133             return hres;
1134         }
1135
1136         match_len = SysStringLen(match_str);
1137         if(!match_len) {
1138             SysFreeString(match_str);
1139             match_str = NULL;
1140         }
1141     }
1142
1143     hres = create_array(ctx, 0, &array);
1144
1145     if(SUCCEEDED(hres)) {
1146         ptr = str;
1147         for(i=0; i<limit; i++) {
1148             if(use_regexp) {
1149                 if(i == match_cnt)
1150                     break;
1151                 ptr2 = match_result[i].str;
1152             }else if(match_str) {
1153                 ptr2 = strstrW(ptr, match_str);
1154                 if(!ptr2)
1155                     break;
1156             }else {
1157                 if(!*ptr)
1158                     break;
1159                 ptr2 = ptr+1;
1160             }
1161
1162             tmp_str = SysAllocStringLen(ptr, ptr2-ptr);
1163             if(!tmp_str) {
1164                 hres = E_OUTOFMEMORY;
1165                 break;
1166             }
1167
1168             hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1169             SysFreeString(tmp_str);
1170             if(FAILED(hres))
1171                 break;
1172
1173             if(use_regexp)
1174                 ptr = match_result[i].str + match_result[i].len;
1175             else if(match_str)
1176                 ptr = ptr2 + match_len;
1177             else
1178                 ptr++;
1179         }
1180     }
1181
1182     if(SUCCEEDED(hres) && (match_str || use_regexp) && i<limit) {
1183         DWORD len = (str+length) - ptr;
1184
1185         if(len || match_str) {
1186             tmp_str = SysAllocStringLen(ptr, len);
1187
1188             if(tmp_str) {
1189                 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1190                 SysFreeString(tmp_str);
1191             }else {
1192                 hres = E_OUTOFMEMORY;
1193             }
1194         }
1195     }
1196
1197     SysFreeString(match_str);
1198     SysFreeString(val_str);
1199     heap_free(match_result);
1200
1201     if(SUCCEEDED(hres) && r)
1202         *r = jsval_obj(array);
1203     else
1204         jsdisp_release(array);
1205
1206     return hres;
1207 }
1208
1209 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1210         jsval_t *r)
1211 {
1212     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1213     return do_attributeless_tag_format(ctx, jsthis, r, striketagW);
1214 }
1215
1216 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1217         jsval_t *r)
1218 {
1219     static const WCHAR subtagW[] = {'S','U','B',0};
1220     return do_attributeless_tag_format(ctx, jsthis, r, subtagW);
1221 }
1222
1223 /* ECMA-262 3rd Edition    15.5.4.15 */
1224 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1225         jsval_t *r)
1226 {
1227     const WCHAR *str;
1228     BSTR val_str;
1229     INT start=0, end;
1230     DWORD length;
1231     double d;
1232     HRESULT hres;
1233
1234     TRACE("\n");
1235
1236     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1237     if(FAILED(hres))
1238         return hres;
1239
1240     if(argc >= 1) {
1241         hres = to_integer(ctx, argv[0], &d);
1242         if(FAILED(hres)) {
1243             SysFreeString(val_str);
1244             return hres;
1245         }
1246
1247         if(d >= 0)
1248             start = is_int32(d) ? min(length, d) : length;
1249     }
1250
1251     if(argc >= 2) {
1252         hres = to_integer(ctx, argv[1], &d);
1253         if(FAILED(hres)) {
1254             SysFreeString(val_str);
1255             return hres;
1256         }
1257
1258         if(d >= 0)
1259             end = is_int32(d) ? min(length, d) : length;
1260         else
1261             end = 0;
1262     }else {
1263         end = length;
1264     }
1265
1266     if(start > end) {
1267         INT tmp = start;
1268         start = end;
1269         end = tmp;
1270     }
1271
1272     if(r) {
1273         BSTR ret = SysAllocStringLen(str+start, end-start);
1274         if(!ret) {
1275             SysFreeString(val_str);
1276             return E_OUTOFMEMORY;
1277         }
1278         *r = jsval_string(ret);
1279     }
1280     SysFreeString(val_str);
1281     return S_OK;
1282 }
1283
1284 /* ECMA-262 3rd Edition    B.2.3 */
1285 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1286         jsval_t *r)
1287 {
1288     BSTR val_str;
1289     const WCHAR *str;
1290     INT start=0, len;
1291     DWORD length;
1292     double d;
1293     HRESULT hres;
1294
1295     TRACE("\n");
1296
1297     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1298     if(FAILED(hres))
1299         return hres;
1300
1301     if(argc >= 1) {
1302         hres = to_integer(ctx, argv[0], &d);
1303         if(FAILED(hres)) {
1304             SysFreeString(val_str);
1305             return hres;
1306         }
1307
1308         if(d >= 0)
1309             start = is_int32(d) ? min(length, d) : length;
1310     }
1311
1312     if(argc >= 2) {
1313         hres = to_integer(ctx, argv[1], &d);
1314         if(FAILED(hres)) {
1315             SysFreeString(val_str);
1316             return hres;
1317         }
1318
1319         if(d >= 0.0)
1320             len = is_int32(d) ? min(length-start, d) : length-start;
1321         else
1322             len = 0;
1323     }else {
1324         len = length-start;
1325     }
1326
1327     hres = S_OK;
1328     if(r) {
1329         BSTR ret = SysAllocStringLen(str+start, len);
1330         if(ret)
1331             *r = jsval_string(ret);
1332         else
1333             hres = E_OUTOFMEMORY;
1334     }
1335
1336     SysFreeString(val_str);
1337     return hres;
1338 }
1339
1340 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1341         jsval_t *r)
1342 {
1343     static const WCHAR suptagW[] = {'S','U','P',0};
1344     return do_attributeless_tag_format(ctx, jsthis, r, suptagW);
1345 }
1346
1347 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1348         jsval_t *r)
1349 {
1350     const WCHAR* str;
1351     DWORD length;
1352     BSTR val_str;
1353     HRESULT  hres;
1354
1355     TRACE("\n");
1356
1357     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1358     if(FAILED(hres))
1359         return hres;
1360
1361     if(r) {
1362         if(!val_str) {
1363             val_str = SysAllocStringLen(str, length);
1364             if(!val_str)
1365                 return E_OUTOFMEMORY;
1366         }
1367
1368         strlwrW(val_str);
1369         *r = jsval_string(val_str);
1370     }
1371     else SysFreeString(val_str);
1372     return S_OK;
1373 }
1374
1375 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1376         jsval_t *r)
1377 {
1378     const WCHAR* str;
1379     DWORD length;
1380     BSTR val_str;
1381     HRESULT hres;
1382
1383     TRACE("\n");
1384
1385     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1386     if(FAILED(hres))
1387         return hres;
1388
1389     if(r) {
1390         if(!val_str) {
1391             val_str = SysAllocStringLen(str, length);
1392             if(!val_str)
1393                 return E_OUTOFMEMORY;
1394         }
1395
1396         struprW(val_str);
1397         *r = jsval_string(val_str);
1398     }
1399     else SysFreeString(val_str);
1400     return S_OK;
1401 }
1402
1403 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1404         jsval_t *r)
1405 {
1406     FIXME("\n");
1407     return E_NOTIMPL;
1408 }
1409
1410 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1411         jsval_t *r)
1412 {
1413     FIXME("\n");
1414     return E_NOTIMPL;
1415 }
1416
1417 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1418         jsval_t *r)
1419 {
1420     FIXME("\n");
1421     return E_NOTIMPL;
1422 }
1423
1424 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1425         jsval_t *r)
1426 {
1427     StringInstance *This = string_from_vdisp(jsthis);
1428
1429     TRACE("\n");
1430
1431     switch(flags) {
1432     case INVOKE_FUNC:
1433         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
1434     case DISPATCH_PROPERTYGET: {
1435         BSTR str = SysAllocString(This->str);
1436         if(!str)
1437             return E_OUTOFMEMORY;
1438
1439         *r = jsval_string(str);
1440         break;
1441     }
1442     default:
1443         FIXME("flags %x\n", flags);
1444         return E_NOTIMPL;
1445     }
1446
1447     return S_OK;
1448 }
1449
1450 static void String_destructor(jsdisp_t *dispex)
1451 {
1452     StringInstance *This = (StringInstance*)dispex;
1453
1454     heap_free(This->str);
1455     heap_free(This);
1456 }
1457
1458 static const builtin_prop_t String_props[] = {
1459     {anchorW,                String_anchor,                PROPF_METHOD|1},
1460     {bigW,                   String_big,                   PROPF_METHOD},
1461     {blinkW,                 String_blink,                 PROPF_METHOD},
1462     {boldW,                  String_bold,                  PROPF_METHOD},
1463     {charAtW,                String_charAt,                PROPF_METHOD|1},
1464     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1465     {concatW,                String_concat,                PROPF_METHOD|1},
1466     {fixedW,                 String_fixed,                 PROPF_METHOD},
1467     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1468     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1469     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1470     {italicsW,               String_italics,               PROPF_METHOD},
1471     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1472     {lengthW,                String_length,                0},
1473     {linkW,                  String_link,                  PROPF_METHOD|1},
1474     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1475     {matchW,                 String_match,                 PROPF_METHOD|1},
1476     {replaceW,               String_replace,               PROPF_METHOD|1},
1477     {searchW,                String_search,                PROPF_METHOD},
1478     {sliceW,                 String_slice,                 PROPF_METHOD},
1479     {smallW,                 String_small,                 PROPF_METHOD},
1480     {splitW,                 String_split,                 PROPF_METHOD|2},
1481     {strikeW,                String_strike,                PROPF_METHOD},
1482     {subW,                   String_sub,                   PROPF_METHOD},
1483     {substrW,                String_substr,                PROPF_METHOD|2},
1484     {substringW,             String_substring,             PROPF_METHOD|2},
1485     {supW,                   String_sup,                   PROPF_METHOD},
1486     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1487     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1488     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1489     {toStringW,              String_toString,              PROPF_METHOD},
1490     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1491     {valueOfW,               String_valueOf,               PROPF_METHOD}
1492 };
1493
1494 static const builtin_info_t String_info = {
1495     JSCLASS_STRING,
1496     {NULL, String_value, 0},
1497     sizeof(String_props)/sizeof(*String_props),
1498     String_props,
1499     String_destructor,
1500     NULL
1501 };
1502
1503 static const builtin_prop_t StringInst_props[] = {
1504     {lengthW,                String_length,                0}
1505 };
1506
1507 static const builtin_info_t StringInst_info = {
1508     JSCLASS_STRING,
1509     {NULL, String_value, 0},
1510     sizeof(StringInst_props)/sizeof(*StringInst_props),
1511     StringInst_props,
1512     String_destructor,
1513     NULL
1514 };
1515
1516 /* ECMA-262 3rd Edition    15.5.3.2 */
1517 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1518         unsigned argc, jsval_t *argv, jsval_t *r)
1519 {
1520     DWORD i, code;
1521     BSTR ret;
1522     HRESULT hres;
1523
1524     TRACE("\n");
1525
1526     ret = SysAllocStringLen(NULL, argc);
1527     if(!ret)
1528         return E_OUTOFMEMORY;
1529
1530     for(i=0; i<argc; i++) {
1531         hres = to_uint32(ctx, argv[i], &code);
1532         if(FAILED(hres)) {
1533             SysFreeString(ret);
1534             return hres;
1535         }
1536
1537         ret[i] = code;
1538     }
1539
1540     if(r)
1541         *r = jsval_string(ret);
1542     else
1543         SysFreeString(ret);
1544     return S_OK;
1545 }
1546
1547 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1548         jsval_t *r)
1549 {
1550     HRESULT hres;
1551
1552     TRACE("\n");
1553
1554     switch(flags) {
1555     case INVOKE_FUNC: {
1556         BSTR str;
1557
1558         if(argc) {
1559             hres = to_string(ctx, argv[0], &str);
1560             if(FAILED(hres))
1561                 return hres;
1562         }else {
1563             str = SysAllocStringLen(NULL, 0);
1564             if(!str)
1565                 return E_OUTOFMEMORY;
1566         }
1567
1568         *r = jsval_string(str);
1569         break;
1570     }
1571     case DISPATCH_CONSTRUCT: {
1572         jsdisp_t *ret;
1573
1574         if(argc) {
1575             BSTR str;
1576
1577             hres = to_string(ctx, argv[0], &str);
1578             if(FAILED(hres))
1579                 return hres;
1580
1581             hres = create_string(ctx, str, SysStringLen(str), &ret);
1582             SysFreeString(str);
1583         }else {
1584             hres = create_string(ctx, NULL, 0, &ret);
1585         }
1586
1587         if(FAILED(hres))
1588             return hres;
1589
1590         *r = jsval_obj(ret);
1591         break;
1592     }
1593
1594     default:
1595         FIXME("unimplemented flags: %x\n", flags);
1596         return E_NOTIMPL;
1597     }
1598
1599     return S_OK;
1600 }
1601
1602 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, StringInstance **ret)
1603 {
1604     StringInstance *string;
1605     HRESULT hres;
1606
1607     string = heap_alloc_zero(sizeof(StringInstance));
1608     if(!string)
1609         return E_OUTOFMEMORY;
1610
1611     if(object_prototype)
1612         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1613     else
1614         hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1615     if(FAILED(hres)) {
1616         heap_free(string);
1617         return hres;
1618     }
1619
1620     *ret = string;
1621     return S_OK;
1622 }
1623
1624 static const builtin_prop_t StringConstr_props[] = {
1625     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1626 };
1627
1628 static const builtin_info_t StringConstr_info = {
1629     JSCLASS_FUNCTION,
1630     {NULL, Function_value, 0},
1631     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1632     StringConstr_props,
1633     NULL,
1634     NULL
1635 };
1636
1637 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1638 {
1639     StringInstance *string;
1640     HRESULT hres;
1641
1642     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1643
1644     hres = string_alloc(ctx, object_prototype, &string);
1645     if(FAILED(hres))
1646         return hres;
1647
1648     hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1649             PROPF_CONSTR|1, &string->dispex, ret);
1650
1651     jsdisp_release(&string->dispex);
1652     return hres;
1653 }
1654
1655 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, jsdisp_t **ret)
1656 {
1657     StringInstance *string;
1658     HRESULT hres;
1659
1660     hres = string_alloc(ctx, NULL, &string);
1661     if(FAILED(hres))
1662         return hres;
1663
1664     if(len == -1)
1665         len = strlenW(str);
1666
1667     string->length = len;
1668     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1669     if(!string->str) {
1670         jsdisp_release(&string->dispex);
1671         return E_OUTOFMEMORY;
1672     }
1673
1674     memcpy(string->str, str, len*sizeof(WCHAR));
1675     string->str[len] = 0;
1676
1677     *ret = &string->dispex;
1678     return S_OK;
1679
1680 }