wbemprox: Add support for uncommitted instances in IWbemClassObject::Get.
[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;
1085     DWORD length, i, match_len = 0;
1086     const WCHAR *str, *ptr, *ptr2, *cp;
1087     unsigned limit = UINT32_MAX;
1088     jsdisp_t *array, *regexp = NULL;
1089     BSTR val_str, match_str = NULL, tmp_str;
1090     HRESULT hres;
1091
1092     TRACE("\n");
1093
1094     if(argc != 1 && argc != 2) {
1095         FIXME("unsupported argc %u\n", argc);
1096         return E_NOTIMPL;
1097     }
1098
1099     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1100     if(FAILED(hres))
1101         return hres;
1102
1103     if(argc > 1 && !is_undefined(argv[1])) {
1104         hres = to_uint32(ctx, argv[1], &limit);
1105         if(FAILED(hres)) {
1106             SysFreeString(val_str);
1107             return hres;
1108         }
1109     }
1110
1111     if(is_object_instance(argv[0])) {
1112         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
1113         if(regexp) {
1114             if(!is_class(regexp, JSCLASS_REGEXP)) {
1115                 jsdisp_release(regexp);
1116                 regexp = NULL;
1117             }
1118         }
1119     }
1120
1121     if(!regexp) {
1122         hres = to_string(ctx, argv[0], &match_str);
1123         if(FAILED(hres)) {
1124             SysFreeString(val_str);
1125             return hres;
1126         }
1127
1128         match_len = SysStringLen(match_str);
1129         if(!match_len) {
1130             SysFreeString(match_str);
1131             match_str = NULL;
1132         }
1133     }
1134
1135     hres = create_array(ctx, 0, &array);
1136
1137     if(SUCCEEDED(hres)) {
1138         ptr = cp = str;
1139         for(i=0; i<limit; i++) {
1140             if(regexp) {
1141                 hres = regexp_match_next(ctx, regexp, 0, str, length, &cp, NULL, NULL, NULL, &match_result);
1142                 if(hres != S_OK)
1143                     break;
1144                 ptr2 = match_result.str;
1145             }else if(match_str) {
1146                 ptr2 = strstrW(ptr, match_str);
1147                 if(!ptr2)
1148                     break;
1149             }else {
1150                 if(!*ptr)
1151                     break;
1152                 ptr2 = ptr+1;
1153             }
1154
1155             tmp_str = SysAllocStringLen(ptr, ptr2-ptr);
1156             if(!tmp_str) {
1157                 hres = E_OUTOFMEMORY;
1158                 break;
1159             }
1160
1161             hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1162             SysFreeString(tmp_str);
1163             if(FAILED(hres))
1164                 break;
1165
1166             if(regexp)
1167                 ptr = cp;
1168             else if(match_str)
1169                 ptr = ptr2 + match_len;
1170             else
1171                 ptr++;
1172         }
1173     }
1174
1175     if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1176         DWORD len = (str+length) - ptr;
1177
1178         if(len || match_str) {
1179             tmp_str = SysAllocStringLen(ptr, len);
1180
1181             if(tmp_str) {
1182                 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1183                 SysFreeString(tmp_str);
1184             }else {
1185                 hres = E_OUTOFMEMORY;
1186             }
1187         }
1188     }
1189
1190     if(regexp)
1191         jsdisp_release(regexp);
1192     SysFreeString(match_str);
1193     SysFreeString(val_str);
1194
1195     if(SUCCEEDED(hres) && r)
1196         *r = jsval_obj(array);
1197     else
1198         jsdisp_release(array);
1199
1200     return hres;
1201 }
1202
1203 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1204         jsval_t *r)
1205 {
1206     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1207     return do_attributeless_tag_format(ctx, jsthis, r, striketagW);
1208 }
1209
1210 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1211         jsval_t *r)
1212 {
1213     static const WCHAR subtagW[] = {'S','U','B',0};
1214     return do_attributeless_tag_format(ctx, jsthis, r, subtagW);
1215 }
1216
1217 /* ECMA-262 3rd Edition    15.5.4.15 */
1218 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1219         jsval_t *r)
1220 {
1221     const WCHAR *str;
1222     BSTR val_str;
1223     INT start=0, end;
1224     DWORD length;
1225     double d;
1226     HRESULT hres;
1227
1228     TRACE("\n");
1229
1230     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1231     if(FAILED(hres))
1232         return hres;
1233
1234     if(argc >= 1) {
1235         hres = to_integer(ctx, argv[0], &d);
1236         if(FAILED(hres)) {
1237             SysFreeString(val_str);
1238             return hres;
1239         }
1240
1241         if(d >= 0)
1242             start = is_int32(d) ? min(length, d) : length;
1243     }
1244
1245     if(argc >= 2) {
1246         hres = to_integer(ctx, argv[1], &d);
1247         if(FAILED(hres)) {
1248             SysFreeString(val_str);
1249             return hres;
1250         }
1251
1252         if(d >= 0)
1253             end = is_int32(d) ? min(length, d) : length;
1254         else
1255             end = 0;
1256     }else {
1257         end = length;
1258     }
1259
1260     if(start > end) {
1261         INT tmp = start;
1262         start = end;
1263         end = tmp;
1264     }
1265
1266     if(r) {
1267         BSTR ret = SysAllocStringLen(str+start, end-start);
1268         if(!ret) {
1269             SysFreeString(val_str);
1270             return E_OUTOFMEMORY;
1271         }
1272         *r = jsval_string(ret);
1273     }
1274     SysFreeString(val_str);
1275     return S_OK;
1276 }
1277
1278 /* ECMA-262 3rd Edition    B.2.3 */
1279 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1280         jsval_t *r)
1281 {
1282     BSTR val_str;
1283     const WCHAR *str;
1284     INT start=0, len;
1285     DWORD length;
1286     double d;
1287     HRESULT hres;
1288
1289     TRACE("\n");
1290
1291     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1292     if(FAILED(hres))
1293         return hres;
1294
1295     if(argc >= 1) {
1296         hres = to_integer(ctx, argv[0], &d);
1297         if(FAILED(hres)) {
1298             SysFreeString(val_str);
1299             return hres;
1300         }
1301
1302         if(d >= 0)
1303             start = is_int32(d) ? min(length, d) : length;
1304     }
1305
1306     if(argc >= 2) {
1307         hres = to_integer(ctx, argv[1], &d);
1308         if(FAILED(hres)) {
1309             SysFreeString(val_str);
1310             return hres;
1311         }
1312
1313         if(d >= 0.0)
1314             len = is_int32(d) ? min(length-start, d) : length-start;
1315         else
1316             len = 0;
1317     }else {
1318         len = length-start;
1319     }
1320
1321     hres = S_OK;
1322     if(r) {
1323         BSTR ret = SysAllocStringLen(str+start, len);
1324         if(ret)
1325             *r = jsval_string(ret);
1326         else
1327             hres = E_OUTOFMEMORY;
1328     }
1329
1330     SysFreeString(val_str);
1331     return hres;
1332 }
1333
1334 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1335         jsval_t *r)
1336 {
1337     static const WCHAR suptagW[] = {'S','U','P',0};
1338     return do_attributeless_tag_format(ctx, jsthis, r, suptagW);
1339 }
1340
1341 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1342         jsval_t *r)
1343 {
1344     const WCHAR* str;
1345     DWORD length;
1346     BSTR val_str;
1347     HRESULT  hres;
1348
1349     TRACE("\n");
1350
1351     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1352     if(FAILED(hres))
1353         return hres;
1354
1355     if(r) {
1356         if(!val_str) {
1357             val_str = SysAllocStringLen(str, length);
1358             if(!val_str)
1359                 return E_OUTOFMEMORY;
1360         }
1361
1362         strlwrW(val_str);
1363         *r = jsval_string(val_str);
1364     }
1365     else SysFreeString(val_str);
1366     return S_OK;
1367 }
1368
1369 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1370         jsval_t *r)
1371 {
1372     const WCHAR* str;
1373     DWORD length;
1374     BSTR val_str;
1375     HRESULT hres;
1376
1377     TRACE("\n");
1378
1379     hres = get_string_val(ctx, jsthis, &str, &length, &val_str);
1380     if(FAILED(hres))
1381         return hres;
1382
1383     if(r) {
1384         if(!val_str) {
1385             val_str = SysAllocStringLen(str, length);
1386             if(!val_str)
1387                 return E_OUTOFMEMORY;
1388         }
1389
1390         struprW(val_str);
1391         *r = jsval_string(val_str);
1392     }
1393     else SysFreeString(val_str);
1394     return S_OK;
1395 }
1396
1397 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1398         jsval_t *r)
1399 {
1400     FIXME("\n");
1401     return E_NOTIMPL;
1402 }
1403
1404 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1405         jsval_t *r)
1406 {
1407     FIXME("\n");
1408     return E_NOTIMPL;
1409 }
1410
1411 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1412         jsval_t *r)
1413 {
1414     FIXME("\n");
1415     return E_NOTIMPL;
1416 }
1417
1418 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1419         jsval_t *r)
1420 {
1421     StringInstance *This = string_from_vdisp(jsthis);
1422
1423     TRACE("\n");
1424
1425     switch(flags) {
1426     case INVOKE_FUNC:
1427         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
1428     case DISPATCH_PROPERTYGET: {
1429         BSTR str = SysAllocString(This->str);
1430         if(!str)
1431             return E_OUTOFMEMORY;
1432
1433         *r = jsval_string(str);
1434         break;
1435     }
1436     default:
1437         FIXME("flags %x\n", flags);
1438         return E_NOTIMPL;
1439     }
1440
1441     return S_OK;
1442 }
1443
1444 static void String_destructor(jsdisp_t *dispex)
1445 {
1446     StringInstance *This = (StringInstance*)dispex;
1447
1448     heap_free(This->str);
1449     heap_free(This);
1450 }
1451
1452 static const builtin_prop_t String_props[] = {
1453     {anchorW,                String_anchor,                PROPF_METHOD|1},
1454     {bigW,                   String_big,                   PROPF_METHOD},
1455     {blinkW,                 String_blink,                 PROPF_METHOD},
1456     {boldW,                  String_bold,                  PROPF_METHOD},
1457     {charAtW,                String_charAt,                PROPF_METHOD|1},
1458     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1459     {concatW,                String_concat,                PROPF_METHOD|1},
1460     {fixedW,                 String_fixed,                 PROPF_METHOD},
1461     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1462     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1463     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1464     {italicsW,               String_italics,               PROPF_METHOD},
1465     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1466     {lengthW,                String_length,                0},
1467     {linkW,                  String_link,                  PROPF_METHOD|1},
1468     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1469     {matchW,                 String_match,                 PROPF_METHOD|1},
1470     {replaceW,               String_replace,               PROPF_METHOD|1},
1471     {searchW,                String_search,                PROPF_METHOD},
1472     {sliceW,                 String_slice,                 PROPF_METHOD},
1473     {smallW,                 String_small,                 PROPF_METHOD},
1474     {splitW,                 String_split,                 PROPF_METHOD|2},
1475     {strikeW,                String_strike,                PROPF_METHOD},
1476     {subW,                   String_sub,                   PROPF_METHOD},
1477     {substrW,                String_substr,                PROPF_METHOD|2},
1478     {substringW,             String_substring,             PROPF_METHOD|2},
1479     {supW,                   String_sup,                   PROPF_METHOD},
1480     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1481     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1482     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1483     {toStringW,              String_toString,              PROPF_METHOD},
1484     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1485     {valueOfW,               String_valueOf,               PROPF_METHOD}
1486 };
1487
1488 static const builtin_info_t String_info = {
1489     JSCLASS_STRING,
1490     {NULL, String_value, 0},
1491     sizeof(String_props)/sizeof(*String_props),
1492     String_props,
1493     String_destructor,
1494     NULL
1495 };
1496
1497 static const builtin_prop_t StringInst_props[] = {
1498     {lengthW,                String_length,                0}
1499 };
1500
1501 static const builtin_info_t StringInst_info = {
1502     JSCLASS_STRING,
1503     {NULL, String_value, 0},
1504     sizeof(StringInst_props)/sizeof(*StringInst_props),
1505     StringInst_props,
1506     String_destructor,
1507     NULL
1508 };
1509
1510 /* ECMA-262 3rd Edition    15.5.3.2 */
1511 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1512         unsigned argc, jsval_t *argv, jsval_t *r)
1513 {
1514     DWORD i, code;
1515     BSTR ret;
1516     HRESULT hres;
1517
1518     TRACE("\n");
1519
1520     ret = SysAllocStringLen(NULL, argc);
1521     if(!ret)
1522         return E_OUTOFMEMORY;
1523
1524     for(i=0; i<argc; i++) {
1525         hres = to_uint32(ctx, argv[i], &code);
1526         if(FAILED(hres)) {
1527             SysFreeString(ret);
1528             return hres;
1529         }
1530
1531         ret[i] = code;
1532     }
1533
1534     if(r)
1535         *r = jsval_string(ret);
1536     else
1537         SysFreeString(ret);
1538     return S_OK;
1539 }
1540
1541 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1542         jsval_t *r)
1543 {
1544     HRESULT hres;
1545
1546     TRACE("\n");
1547
1548     switch(flags) {
1549     case INVOKE_FUNC: {
1550         BSTR str;
1551
1552         if(argc) {
1553             hres = to_string(ctx, argv[0], &str);
1554             if(FAILED(hres))
1555                 return hres;
1556         }else {
1557             str = SysAllocStringLen(NULL, 0);
1558             if(!str)
1559                 return E_OUTOFMEMORY;
1560         }
1561
1562         *r = jsval_string(str);
1563         break;
1564     }
1565     case DISPATCH_CONSTRUCT: {
1566         jsdisp_t *ret;
1567
1568         if(argc) {
1569             BSTR str;
1570
1571             hres = to_string(ctx, argv[0], &str);
1572             if(FAILED(hres))
1573                 return hres;
1574
1575             hres = create_string(ctx, str, SysStringLen(str), &ret);
1576             SysFreeString(str);
1577         }else {
1578             hres = create_string(ctx, NULL, 0, &ret);
1579         }
1580
1581         if(FAILED(hres))
1582             return hres;
1583
1584         *r = jsval_obj(ret);
1585         break;
1586     }
1587
1588     default:
1589         FIXME("unimplemented flags: %x\n", flags);
1590         return E_NOTIMPL;
1591     }
1592
1593     return S_OK;
1594 }
1595
1596 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, StringInstance **ret)
1597 {
1598     StringInstance *string;
1599     HRESULT hres;
1600
1601     string = heap_alloc_zero(sizeof(StringInstance));
1602     if(!string)
1603         return E_OUTOFMEMORY;
1604
1605     if(object_prototype)
1606         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1607     else
1608         hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1609     if(FAILED(hres)) {
1610         heap_free(string);
1611         return hres;
1612     }
1613
1614     *ret = string;
1615     return S_OK;
1616 }
1617
1618 static const builtin_prop_t StringConstr_props[] = {
1619     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1620 };
1621
1622 static const builtin_info_t StringConstr_info = {
1623     JSCLASS_FUNCTION,
1624     {NULL, Function_value, 0},
1625     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1626     StringConstr_props,
1627     NULL,
1628     NULL
1629 };
1630
1631 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1632 {
1633     StringInstance *string;
1634     HRESULT hres;
1635
1636     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1637
1638     hres = string_alloc(ctx, object_prototype, &string);
1639     if(FAILED(hres))
1640         return hres;
1641
1642     hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1643             PROPF_CONSTR|1, &string->dispex, ret);
1644
1645     jsdisp_release(&string->dispex);
1646     return hres;
1647 }
1648
1649 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, jsdisp_t **ret)
1650 {
1651     StringInstance *string;
1652     HRESULT hres;
1653
1654     hres = string_alloc(ctx, NULL, &string);
1655     if(FAILED(hres))
1656         return hres;
1657
1658     if(len == -1)
1659         len = strlenW(str);
1660
1661     string->length = len;
1662     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1663     if(!string->str) {
1664         jsdisp_release(&string->dispex);
1665         return E_OUTOFMEMORY;
1666     }
1667
1668     memcpy(string->str, str, len*sizeof(WCHAR));
1669     string->str[len] = 0;
1670
1671     *ret = &string->dispex;
1672     return S_OK;
1673
1674 }