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