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