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