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