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