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