setupapi: Add stub keyword to some FIXMEs.
[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     unsigned len = 0, i, str_cnt;
381     jsstr_t **strs, *ret = NULL;
382     WCHAR *ptr;
383     HRESULT hres;
384
385     TRACE("\n");
386
387     str_cnt = argc+1;
388     strs = heap_alloc_zero(str_cnt * sizeof(*strs));
389     if(!strs)
390         return E_OUTOFMEMORY;
391
392     hres = to_string(ctx, jsval_disp(jsthis->u.disp), strs);
393     if(SUCCEEDED(hres)) {
394         for(i=0; i < argc; i++) {
395             hres = to_string(ctx, argv[i], strs+i+1);
396             if(FAILED(hres))
397                 break;
398         }
399     }
400
401     if(SUCCEEDED(hres)) {
402         for(i=0; i < str_cnt; i++) {
403             len += jsstr_length(strs[i]);
404             if(len > JSSTR_MAX_LENGTH) {
405                 hres = E_OUTOFMEMORY;
406                 break;
407             }
408         }
409
410         ptr = jsstr_alloc_buf(len, &ret);
411         if(ptr) {
412             for(i=0; i < str_cnt; i++)
413                 ptr += jsstr_flush(strs[i], ptr);
414         }else {
415             hres = E_OUTOFMEMORY;
416         }
417     }
418
419     for(i=0; i < str_cnt; i++)
420         jsstr_release(strs[i]);
421     heap_free(strs);
422
423     if(FAILED(hres))
424         return hres;
425
426     if(r)
427         *r = jsval_string(ret);
428     else
429         jsstr_release(ret);
430     return S_OK;
431 }
432
433 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
434         jsval_t *r)
435 {
436     static const WCHAR fixedtagW[] = {'T','T',0};
437     return do_attributeless_tag_format(ctx, jsthis, r, fixedtagW);
438 }
439
440 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
441         jsval_t *r)
442 {
443     static const WCHAR fontW[] = {'F','O','N','T',0};
444     static const WCHAR colorW[] = {'C','O','L','O','R',0};
445
446     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
447 }
448
449 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
450         jsval_t *r)
451 {
452     static const WCHAR fontW[] = {'F','O','N','T',0};
453     static const WCHAR colorW[] = {'S','I','Z','E',0};
454
455     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
456 }
457
458 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
459         jsval_t *r)
460 {
461     jsstr_t *search_jsstr, *jsstr;
462     const WCHAR *search_str, *str;
463     int length, pos = 0;
464     INT ret = -1;
465     HRESULT hres;
466
467     TRACE("\n");
468
469     hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
470     if(FAILED(hres))
471         return hres;
472
473     length = jsstr_length(jsstr);
474     if(!argc) {
475         if(r)
476             *r = jsval_number(-1);
477         jsstr_release(jsstr);
478         return S_OK;
479     }
480
481     hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
482     if(FAILED(hres)) {
483         jsstr_release(jsstr);
484         return hres;
485     }
486
487     if(argc >= 2) {
488         double d;
489
490         hres = to_integer(ctx, argv[1], &d);
491         if(SUCCEEDED(hres) && d > 0.0)
492             pos = is_int32(d) ? min(length, d) : length;
493     }
494
495     if(SUCCEEDED(hres)) {
496         const WCHAR *ptr;
497
498         ptr = strstrW(str+pos, search_str);
499         if(ptr)
500             ret = ptr - str;
501         else
502             ret = -1;
503     }
504
505     jsstr_release(search_jsstr);
506     jsstr_release(jsstr);
507     if(FAILED(hres))
508         return hres;
509
510     if(r)
511         *r = jsval_number(ret);
512     return S_OK;
513 }
514
515 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
516         jsval_t *r)
517 {
518     static const WCHAR italicstagW[] = {'I',0};
519     return do_attributeless_tag_format(ctx, jsthis, r, italicstagW);
520 }
521
522 /* ECMA-262 3rd Edition    15.5.4.8 */
523 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
524         jsval_t *r)
525 {
526     unsigned pos = 0, search_len, length;
527     jsstr_t *search_jsstr, *jsstr;
528     const WCHAR *search_str, *str;
529     INT ret = -1;
530     HRESULT hres;
531
532     TRACE("\n");
533
534     hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
535     if(FAILED(hres))
536         return hres;
537
538     if(!argc) {
539         if(r)
540             *r = jsval_number(-1);
541         jsstr_release(jsstr);
542         return S_OK;
543     }
544
545     hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
546     if(FAILED(hres)) {
547         jsstr_release(jsstr);
548         return hres;
549     }
550
551     search_len = jsstr_length(search_jsstr);
552     length = jsstr_length(jsstr);
553
554     if(argc >= 2) {
555         double d;
556
557         hres = to_integer(ctx, argv[1], &d);
558         if(SUCCEEDED(hres) && d > 0)
559             pos = is_int32(d) ? min(length, d) : length;
560     }else {
561         pos = length;
562     }
563
564     if(SUCCEEDED(hres) && length >= search_len) {
565         const WCHAR *ptr;
566
567         for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) {
568             if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
569                 ret = ptr-str;
570                 break;
571             }
572         }
573     }
574
575     jsstr_release(search_jsstr);
576     jsstr_release(jsstr);
577     if(FAILED(hres))
578         return hres;
579
580     if(r)
581         *r = jsval_number(ret);
582     return S_OK;
583 }
584
585 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
586         jsval_t *r)
587 {
588     static const WCHAR fontW[] = {'A',0};
589     static const WCHAR colorW[] = {'H','R','E','F',0};
590
591     return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
592 }
593
594 /* ECMA-262 3rd Edition    15.5.4.10 */
595 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
596         jsval_t *r)
597 {
598     jsdisp_t *regexp = NULL;
599     jsstr_t *str;
600     HRESULT hres;
601
602     TRACE("\n");
603
604     if(!argc) {
605         if(r)
606             *r = jsval_null();
607         return S_OK;
608     }
609
610     if(is_object_instance(argv[0])) {
611         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
612         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
613             jsdisp_release(regexp);
614             regexp = NULL;
615         }
616     }
617
618     if(!regexp) {
619         jsstr_t *match_str;
620
621         hres = to_string(ctx, argv[0], &match_str);
622         if(FAILED(hres))
623             return hres;
624
625         hres = create_regexp(ctx, match_str, 0, &regexp);
626         jsstr_release(match_str);
627         if(FAILED(hres))
628             return hres;
629     }
630
631     hres = get_string_val(ctx, jsthis, &str);
632     if(SUCCEEDED(hres))
633         hres = regexp_string_match(ctx, regexp, str, r);
634
635     jsdisp_release(regexp);
636     jsstr_release(str);
637     return hres;
638 }
639
640 typedef struct {
641     WCHAR *buf;
642     DWORD size;
643     DWORD len;
644 } strbuf_t;
645
646 static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len)
647 {
648     WCHAR *new_buf;
649     DWORD new_size;
650
651     if(len <= buf->size)
652         return TRUE;
653
654     new_size = buf->size ? buf->size<<1 : 16;
655     if(new_size < len)
656         new_size = len;
657     if(buf->buf)
658         new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
659     else
660         new_buf = heap_alloc(new_size*sizeof(WCHAR));
661     if(!new_buf)
662         return FALSE;
663
664     buf->buf = new_buf;
665     buf->size = new_size;
666     return TRUE;
667 }
668
669 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
670 {
671     if(!len)
672         return S_OK;
673
674     if(!strbuf_ensure_size(buf, buf->len+len))
675         return E_OUTOFMEMORY;
676
677     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
678     buf->len += len;
679     return S_OK;
680 }
681
682 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str)
683 {
684     if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str)))
685         return E_OUTOFMEMORY;
686
687     jsstr_flush(str, buf->buf+buf->len);
688     buf->len += jsstr_length(str);
689     return S_OK;
690 }
691
692 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
693         jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret)
694 {
695     jsval_t *argv;
696     unsigned argc;
697     jsval_t val;
698     jsstr_t *tmp_str;
699     DWORD i;
700     HRESULT hres = S_OK;
701
702     argc = match->paren_count+3;
703     argv = heap_alloc_zero(sizeof(*argv)*argc);
704     if(!argv)
705         return E_OUTOFMEMORY;
706
707     tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
708     if(!tmp_str)
709         hres = E_OUTOFMEMORY;
710     argv[0] = jsval_string(tmp_str);
711
712     if(SUCCEEDED(hres)) {
713         for(i=0; i < match->paren_count; i++) {
714             if(match->parens[i].index != -1)
715                 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length);
716             else
717                 tmp_str = jsstr_empty();
718             if(!tmp_str) {
719                hres = E_OUTOFMEMORY;
720                break;
721             }
722             argv[i+1] = jsval_string(tmp_str);
723         }
724     }
725
726     if(SUCCEEDED(hres)) {
727         argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len);
728         argv[match->paren_count+2] = jsval_string(jsstr);
729     }
730
731     if(SUCCEEDED(hres))
732         hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val);
733
734     for(i=0; i <= match->paren_count; i++)
735         jsstr_release(get_string(argv[i]));
736     heap_free(argv);
737
738     if(FAILED(hres))
739         return hres;
740
741     hres = to_string(ctx, val, ret);
742     jsval_release(val);
743     return hres;
744 }
745
746 /* ECMA-262 3rd Edition    15.5.4.11 */
747 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
748         jsval_t *r)
749 {
750     const WCHAR *str, *match_str = NULL, *rep_str = NULL;
751     jsstr_t *rep_jsstr, *match_jsstr, *jsstr;
752     jsdisp_t *rep_func = NULL, *regexp = NULL;
753     match_state_t *match = NULL, last_match = {0};
754     strbuf_t ret = {NULL,0,0};
755     DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
756     DWORD rep_len=0;
757     HRESULT hres = S_OK;
758
759     TRACE("\n");
760
761     hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
762     if(FAILED(hres))
763         return hres;
764
765     if(!argc) {
766         if(r)
767             *r = jsval_string(jsstr);
768         else
769             jsstr_release(jsstr);
770         return S_OK;
771     }
772
773     if(is_object_instance(argv[0])) {
774         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
775         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
776             jsdisp_release(regexp);
777             regexp = NULL;
778         }
779     }
780
781     if(!regexp) {
782         hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
783         if(FAILED(hres)) {
784             jsstr_release(jsstr);
785             return hres;
786         }
787     }
788
789     if(argc >= 2) {
790         if(is_object_instance(argv[1])) {
791             rep_func = iface_to_jsdisp((IUnknown*)get_object(argv[1]));
792             if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
793                 jsdisp_release(rep_func);
794                 rep_func = NULL;
795             }
796         }
797
798         if(!rep_func) {
799             hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str);
800             if(SUCCEEDED(hres))
801                 rep_len = jsstr_length(rep_jsstr);
802         }
803     }
804
805     if(SUCCEEDED(hres)) {
806         const WCHAR *ecp = str;
807
808         while(1) {
809             if(regexp) {
810                 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match);
811                 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
812
813                 if(hres == S_FALSE) {
814                     hres = S_OK;
815                     break;
816                 }
817                 if(FAILED(hres))
818                     break;
819
820                 last_match.cp = match->cp;
821                 last_match.match_len = match->match_len;
822             }else {
823                 if(re_flags & REM_ALLOC_RESULT) {
824                     re_flags &= ~REM_ALLOC_RESULT;
825                     match = &last_match;
826                     match->cp = str;
827                 }
828
829                 match->cp = strstrW(match->cp, match_str);
830                 if(!match->cp)
831                     break;
832                 match->match_len = jsstr_length(match_jsstr);
833                 match->cp += match->match_len;
834             }
835
836             hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
837             ecp = match->cp;
838             if(FAILED(hres))
839                 break;
840
841             if(rep_func) {
842                 jsstr_t *cstr;
843
844                 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr);
845                 if(FAILED(hres))
846                     break;
847
848                 hres = strbuf_append_jsstr(&ret, cstr);
849                 jsstr_release(cstr);
850                 if(FAILED(hres))
851                     break;
852             }else if(rep_str && regexp) {
853                 const WCHAR *ptr = rep_str, *ptr2;
854
855                 while((ptr2 = strchrW(ptr, '$'))) {
856                     hres = strbuf_append(&ret, ptr, ptr2-ptr);
857                     if(FAILED(hres))
858                         break;
859
860                     switch(ptr2[1]) {
861                     case '$':
862                         hres = strbuf_append(&ret, ptr2, 1);
863                         ptr = ptr2+2;
864                         break;
865                     case '&':
866                         hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
867                         ptr = ptr2+2;
868                         break;
869                     case '`':
870                         hres = strbuf_append(&ret, str, match->cp-str-match->match_len);
871                         ptr = ptr2+2;
872                         break;
873                     case '\'':
874                         hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp);
875                         ptr = ptr2+2;
876                         break;
877                     default: {
878                         DWORD idx;
879
880                         if(!isdigitW(ptr2[1])) {
881                             hres = strbuf_append(&ret, ptr2, 1);
882                             ptr = ptr2+1;
883                             break;
884                         }
885
886                         idx = ptr2[1] - '0';
887                         if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
888                             idx = idx*10 + (ptr[2]-'0');
889                             ptr = ptr2+3;
890                         }else if(idx && idx <= match->paren_count) {
891                             ptr = ptr2+2;
892                         }else {
893                             hres = strbuf_append(&ret, ptr2, 1);
894                             ptr = ptr2+1;
895                             break;
896                         }
897
898                         if(match->parens[idx-1].index != -1)
899                             hres = strbuf_append(&ret, str+match->parens[idx-1].index,
900                                     match->parens[idx-1].length);
901                     }
902                     }
903
904                     if(FAILED(hres))
905                         break;
906                 }
907
908                 if(SUCCEEDED(hres))
909                     hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
910                 if(FAILED(hres))
911                     break;
912             }else if(rep_str) {
913                 hres = strbuf_append(&ret, rep_str, rep_len);
914                 if(FAILED(hres))
915                     break;
916             }else {
917                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
918
919                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
920                 if(FAILED(hres))
921                     break;
922             }
923
924             if(!regexp)
925                 break;
926             else if(!match->match_len)
927                 match->cp++;
928         }
929
930         if(SUCCEEDED(hres))
931             hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
932     }
933
934     if(rep_func)
935         jsdisp_release(rep_func);
936     if(rep_str)
937         jsstr_release(rep_jsstr);
938     if(match_str)
939         jsstr_release(match_jsstr);
940     if(regexp)
941         heap_free(match);
942
943     if(SUCCEEDED(hres) && last_match.cp && regexp) {
944         jsstr_release(ctx->last_match);
945         ctx->last_match = jsstr_addref(jsstr);
946         ctx->last_match_index = last_match.cp-str-last_match.match_len;
947         ctx->last_match_length = last_match.match_len;
948     }
949
950     if(regexp)
951         jsdisp_release(regexp);
952     jsstr_release(jsstr);
953
954     if(SUCCEEDED(hres) && r) {
955         jsstr_t *ret_str;
956
957         ret_str = jsstr_alloc_len(ret.buf, ret.len);
958         if(!ret_str)
959             return E_OUTOFMEMORY;
960
961         TRACE("= %s\n", debugstr_jsstr(ret_str));
962         *r = jsval_string(ret_str);
963     }
964
965     heap_free(ret.buf);
966     return hres;
967 }
968
969 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
970         jsval_t *r)
971 {
972     jsdisp_t *regexp = NULL;
973     const WCHAR *str;
974     jsstr_t *jsstr;
975     match_state_t match, *match_ptr = &match;
976     HRESULT hres;
977
978     TRACE("\n");
979
980     hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
981     if(FAILED(hres))
982         return hres;
983
984     if(!argc) {
985         if(r)
986             *r = jsval_null();
987         jsstr_release(jsstr);
988         return S_OK;
989     }
990
991     if(is_object_instance(argv[0])) {
992         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
993         if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
994             jsdisp_release(regexp);
995             regexp = NULL;
996         }
997     }
998
999     if(!regexp) {
1000         hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
1001         if(FAILED(hres)) {
1002             jsstr_release(jsstr);
1003             return hres;
1004         }
1005     }
1006
1007     match.cp = str;
1008     hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
1009     jsstr_release(jsstr);
1010     jsdisp_release(regexp);
1011     if(FAILED(hres))
1012         return hres;
1013
1014     if(r)
1015         *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
1016     return S_OK;
1017 }
1018
1019 /* ECMA-262 3rd Edition    15.5.4.13 */
1020 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1021         jsval_t *r)
1022 {
1023     int start=0, end, length;
1024     jsstr_t *str;
1025     double d;
1026     HRESULT hres;
1027
1028     TRACE("\n");
1029
1030     hres = get_string_val(ctx, jsthis, &str);
1031     if(FAILED(hres))
1032         return hres;
1033
1034     length = jsstr_length(str);
1035     if(argc) {
1036         hres = to_integer(ctx, argv[0], &d);
1037         if(FAILED(hres)) {
1038             jsstr_release(str);
1039             return hres;
1040         }
1041
1042         if(is_int32(d)) {
1043             start = d;
1044             if(start < 0) {
1045                 start = length + start;
1046                 if(start < 0)
1047                     start = 0;
1048             }else if(start > length) {
1049                 start = length;
1050             }
1051         }else if(d > 0) {
1052             start = length;
1053         }
1054     }
1055
1056     if(argc >= 2) {
1057         hres = to_integer(ctx, argv[1], &d);
1058         if(FAILED(hres)) {
1059             jsstr_release(str);
1060             return hres;
1061         }
1062
1063         if(is_int32(d)) {
1064             end = d;
1065             if(end < 0) {
1066                 end = length + end;
1067                 if(end < 0)
1068                     end = 0;
1069             }else if(end > length) {
1070                 end = length;
1071             }
1072         }else {
1073             end = d < 0.0 ? 0 : length;
1074         }
1075     }else {
1076         end = length;
1077     }
1078
1079     if(end < start)
1080         end = start;
1081
1082     if(r) {
1083         jsstr_t *retstr = jsstr_substr(str, start, end-start);
1084         if(!retstr) {
1085             jsstr_release(str);
1086             return E_OUTOFMEMORY;
1087         }
1088
1089         *r = jsval_string(retstr);
1090     }
1091
1092     jsstr_release(str);
1093     return S_OK;
1094 }
1095
1096 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1097         jsval_t *r)
1098 {
1099     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1100     return do_attributeless_tag_format(ctx, jsthis, r, smalltagW);
1101 }
1102
1103 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1104         jsval_t *r)
1105 {
1106     match_state_t match_result, *match_ptr = &match_result;
1107     DWORD length, i, match_len = 0;
1108     const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1109     unsigned limit = UINT32_MAX;
1110     jsdisp_t *array, *regexp = NULL;
1111     jsstr_t *jsstr, *match_jsstr, *tmp_str;
1112     HRESULT hres;
1113
1114     TRACE("\n");
1115
1116     if(argc != 1 && argc != 2) {
1117         FIXME("unsupported argc %u\n", argc);
1118         return E_NOTIMPL;
1119     }
1120
1121     hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
1122     if(FAILED(hres))
1123         return hres;
1124
1125     length = jsstr_length(jsstr);
1126
1127     if(argc > 1 && !is_undefined(argv[1])) {
1128         hres = to_uint32(ctx, argv[1], &limit);
1129         if(FAILED(hres)) {
1130             jsstr_release(jsstr);
1131             return hres;
1132         }
1133     }
1134
1135     if(is_object_instance(argv[0])) {
1136         regexp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
1137         if(regexp) {
1138             if(!is_class(regexp, JSCLASS_REGEXP)) {
1139                 jsdisp_release(regexp);
1140                 regexp = NULL;
1141             }
1142         }
1143     }
1144
1145     if(!regexp) {
1146         hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1147         if(FAILED(hres)) {
1148             jsstr_release(jsstr);
1149             return hres;
1150         }
1151
1152         match_len = jsstr_length(match_jsstr);
1153         if(!match_len) {
1154             jsstr_release(match_jsstr);
1155             match_str = NULL;
1156         }
1157     }
1158
1159     hres = create_array(ctx, 0, &array);
1160
1161     if(SUCCEEDED(hres)) {
1162         ptr = str;
1163         match_result.cp = str;
1164         for(i=0; i<limit; i++) {
1165             if(regexp) {
1166                 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1167                 if(hres != S_OK)
1168                     break;
1169                 ptr2 = match_result.cp - match_result.match_len;
1170             }else if(match_str) {
1171                 ptr2 = strstrW(ptr, match_str);
1172                 if(!ptr2)
1173                     break;
1174             }else {
1175                 if(!*ptr)
1176                     break;
1177                 ptr2 = ptr+1;
1178             }
1179
1180             tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1181             if(!tmp_str) {
1182                 hres = E_OUTOFMEMORY;
1183                 break;
1184             }
1185
1186             hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1187             jsstr_release(tmp_str);
1188             if(FAILED(hres))
1189                 break;
1190
1191             if(regexp)
1192                 ptr = match_result.cp;
1193             else if(match_str)
1194                 ptr = ptr2 + match_len;
1195             else
1196                 ptr++;
1197         }
1198     }
1199
1200     if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1201         DWORD len = (str+length) - ptr;
1202
1203         if(len || match_str) {
1204             tmp_str = jsstr_alloc_len(ptr, len);
1205
1206             if(tmp_str) {
1207                 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1208                 jsstr_release(tmp_str);
1209             }else {
1210                 hres = E_OUTOFMEMORY;
1211             }
1212         }
1213     }
1214
1215     if(regexp)
1216         jsdisp_release(regexp);
1217     if(match_str)
1218         jsstr_release(match_jsstr);
1219     jsstr_release(jsstr);
1220
1221     if(SUCCEEDED(hres) && r)
1222         *r = jsval_obj(array);
1223     else
1224         jsdisp_release(array);
1225
1226     return hres;
1227 }
1228
1229 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1230         jsval_t *r)
1231 {
1232     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1233     return do_attributeless_tag_format(ctx, jsthis, r, striketagW);
1234 }
1235
1236 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1237         jsval_t *r)
1238 {
1239     static const WCHAR subtagW[] = {'S','U','B',0};
1240     return do_attributeless_tag_format(ctx, jsthis, r, subtagW);
1241 }
1242
1243 /* ECMA-262 3rd Edition    15.5.4.15 */
1244 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1245         jsval_t *r)
1246 {
1247     INT start=0, end, length;
1248     jsstr_t *str;
1249     double d;
1250     HRESULT hres;
1251
1252     TRACE("\n");
1253
1254     hres = get_string_val(ctx, jsthis, &str);
1255     if(FAILED(hres))
1256         return hres;
1257
1258     length = jsstr_length(str);
1259     if(argc >= 1) {
1260         hres = to_integer(ctx, argv[0], &d);
1261         if(FAILED(hres)) {
1262             jsstr_release(str);
1263             return hres;
1264         }
1265
1266         if(d >= 0)
1267             start = is_int32(d) ? min(length, d) : length;
1268     }
1269
1270     if(argc >= 2) {
1271         hres = to_integer(ctx, argv[1], &d);
1272         if(FAILED(hres)) {
1273             jsstr_release(str);
1274             return hres;
1275         }
1276
1277         if(d >= 0)
1278             end = is_int32(d) ? min(length, d) : length;
1279         else
1280             end = 0;
1281     }else {
1282         end = length;
1283     }
1284
1285     if(start > end) {
1286         INT tmp = start;
1287         start = end;
1288         end = tmp;
1289     }
1290
1291     if(r) {
1292         jsstr_t *ret = jsstr_substr(str, start, end-start);
1293         if(ret)
1294             *r = jsval_string(ret);
1295         else
1296             hres = E_OUTOFMEMORY;
1297     }
1298     jsstr_release(str);
1299     return hres;
1300 }
1301
1302 /* ECMA-262 3rd Edition    B.2.3 */
1303 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1304         jsval_t *r)
1305 {
1306     int start=0, len, length;
1307     jsstr_t *str;
1308     double d;
1309     HRESULT hres;
1310
1311     TRACE("\n");
1312
1313     hres = get_string_val(ctx, jsthis, &str);
1314     if(FAILED(hres))
1315         return hres;
1316
1317     length = jsstr_length(str);
1318     if(argc >= 1) {
1319         hres = to_integer(ctx, argv[0], &d);
1320         if(FAILED(hres)) {
1321             jsstr_release(str);
1322             return hres;
1323         }
1324
1325         if(d >= 0)
1326             start = is_int32(d) ? min(length, d) : length;
1327     }
1328
1329     if(argc >= 2) {
1330         hres = to_integer(ctx, argv[1], &d);
1331         if(FAILED(hres)) {
1332             jsstr_release(str);
1333             return hres;
1334         }
1335
1336         if(d >= 0.0)
1337             len = is_int32(d) ? min(length-start, d) : length-start;
1338         else
1339             len = 0;
1340     }else {
1341         len = length-start;
1342     }
1343
1344     hres = S_OK;
1345     if(r) {
1346         jsstr_t *ret = jsstr_substr(str, start, len);
1347         if(ret)
1348             *r = jsval_string(ret);
1349         else
1350             hres = E_OUTOFMEMORY;
1351     }
1352
1353     jsstr_release(str);
1354     return hres;
1355 }
1356
1357 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1358         jsval_t *r)
1359 {
1360     static const WCHAR suptagW[] = {'S','U','P',0};
1361     return do_attributeless_tag_format(ctx, jsthis, r, suptagW);
1362 }
1363
1364 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1365         jsval_t *r)
1366 {
1367     jsstr_t *str;
1368     HRESULT  hres;
1369
1370     TRACE("\n");
1371
1372     hres = get_string_val(ctx, jsthis, &str);
1373     if(FAILED(hres))
1374         return hres;
1375
1376     if(r) {
1377         jsstr_t *ret;
1378         WCHAR *buf;
1379
1380         buf = jsstr_alloc_buf(jsstr_length(str), &ret);
1381         if(!buf) {
1382             jsstr_release(str);
1383             return E_OUTOFMEMORY;
1384         }
1385
1386         jsstr_flush(str, buf);
1387         strlwrW(buf);
1388         *r = jsval_string(ret);
1389     }
1390     jsstr_release(str);
1391     return S_OK;
1392 }
1393
1394 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1395         jsval_t *r)
1396 {
1397     jsstr_t *str;
1398     HRESULT hres;
1399
1400     TRACE("\n");
1401
1402     hres = get_string_val(ctx, jsthis, &str);
1403     if(FAILED(hres))
1404         return hres;
1405
1406     if(r) {
1407         jsstr_t *ret;
1408         WCHAR *buf;
1409
1410         buf = jsstr_alloc_buf(jsstr_length(str), &ret);
1411         if(!buf) {
1412             jsstr_release(str);
1413             return E_OUTOFMEMORY;
1414         }
1415
1416         jsstr_flush(str, buf);
1417         struprW(buf);
1418         *r = jsval_string(ret);
1419     }
1420     jsstr_release(str);
1421     return S_OK;
1422 }
1423
1424 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1425         jsval_t *r)
1426 {
1427     FIXME("\n");
1428     return E_NOTIMPL;
1429 }
1430
1431 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1432         jsval_t *r)
1433 {
1434     FIXME("\n");
1435     return E_NOTIMPL;
1436 }
1437
1438 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1439         jsval_t *r)
1440 {
1441     FIXME("\n");
1442     return E_NOTIMPL;
1443 }
1444
1445 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1446         jsval_t *r)
1447 {
1448     StringInstance *This = string_from_vdisp(jsthis);
1449
1450     TRACE("\n");
1451
1452     switch(flags) {
1453     case INVOKE_FUNC:
1454         return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
1455     case DISPATCH_PROPERTYGET: {
1456         *r = jsval_string(jsstr_addref(This->str));
1457         break;
1458     }
1459     default:
1460         FIXME("flags %x\n", flags);
1461         return E_NOTIMPL;
1462     }
1463
1464     return S_OK;
1465 }
1466
1467 static void String_destructor(jsdisp_t *dispex)
1468 {
1469     StringInstance *This = (StringInstance*)dispex;
1470
1471     jsstr_release(This->str);
1472     heap_free(This);
1473 }
1474
1475 static unsigned String_idx_length(jsdisp_t *jsdisp)
1476 {
1477     StringInstance *string = (StringInstance*)jsdisp;
1478
1479     /*
1480      * NOTE: For invoke version < 2, indexed array is not implemented at all.
1481      * Newer jscript.dll versions implement it on string type, not class,
1482      * which is not how it should work according to spec. IE9 implements it
1483      * properly, but it uses its own JavaScript engine inside MSHTML. We
1484      * implement it here, but in the way IE9 and spec work.
1485      */
1486     return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1487 }
1488
1489 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1490 {
1491     StringInstance *string = (StringInstance*)jsdisp;
1492     jsstr_t *ret;
1493
1494     ret = jsstr_substr(string->str, idx, 1);
1495     if(!ret)
1496         return E_OUTOFMEMORY;
1497
1498     TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1499
1500     *r = jsval_string(ret);
1501     return S_OK;
1502 }
1503
1504 static const builtin_prop_t String_props[] = {
1505     {anchorW,                String_anchor,                PROPF_METHOD|1},
1506     {bigW,                   String_big,                   PROPF_METHOD},
1507     {blinkW,                 String_blink,                 PROPF_METHOD},
1508     {boldW,                  String_bold,                  PROPF_METHOD},
1509     {charAtW,                String_charAt,                PROPF_METHOD|1},
1510     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1511     {concatW,                String_concat,                PROPF_METHOD|1},
1512     {fixedW,                 String_fixed,                 PROPF_METHOD},
1513     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1514     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1515     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1516     {italicsW,               String_italics,               PROPF_METHOD},
1517     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1518     {lengthW,                String_length,                0},
1519     {linkW,                  String_link,                  PROPF_METHOD|1},
1520     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1521     {matchW,                 String_match,                 PROPF_METHOD|1},
1522     {replaceW,               String_replace,               PROPF_METHOD|1},
1523     {searchW,                String_search,                PROPF_METHOD},
1524     {sliceW,                 String_slice,                 PROPF_METHOD},
1525     {smallW,                 String_small,                 PROPF_METHOD},
1526     {splitW,                 String_split,                 PROPF_METHOD|2},
1527     {strikeW,                String_strike,                PROPF_METHOD},
1528     {subW,                   String_sub,                   PROPF_METHOD},
1529     {substrW,                String_substr,                PROPF_METHOD|2},
1530     {substringW,             String_substring,             PROPF_METHOD|2},
1531     {supW,                   String_sup,                   PROPF_METHOD},
1532     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1533     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1534     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1535     {toStringW,              String_toString,              PROPF_METHOD},
1536     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1537     {valueOfW,               String_valueOf,               PROPF_METHOD}
1538 };
1539
1540 static const builtin_info_t String_info = {
1541     JSCLASS_STRING,
1542     {NULL, String_value, 0},
1543     sizeof(String_props)/sizeof(*String_props),
1544     String_props,
1545     String_destructor,
1546     NULL
1547 };
1548
1549 static const builtin_prop_t StringInst_props[] = {
1550     {lengthW,                String_length,                0}
1551 };
1552
1553 static const builtin_info_t StringInst_info = {
1554     JSCLASS_STRING,
1555     {NULL, String_value, 0},
1556     sizeof(StringInst_props)/sizeof(*StringInst_props),
1557     StringInst_props,
1558     String_destructor,
1559     NULL,
1560     String_idx_length,
1561     String_idx_get
1562 };
1563
1564 /* ECMA-262 3rd Edition    15.5.3.2 */
1565 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1566         unsigned argc, jsval_t *argv, jsval_t *r)
1567 {
1568     WCHAR *ret_str;
1569     DWORD i, code;
1570     jsstr_t *ret;
1571     HRESULT hres;
1572
1573     TRACE("\n");
1574
1575     ret_str = jsstr_alloc_buf(argc, &ret);
1576     if(!ret_str)
1577         return E_OUTOFMEMORY;
1578
1579     for(i=0; i<argc; i++) {
1580         hres = to_uint32(ctx, argv[i], &code);
1581         if(FAILED(hres)) {
1582             jsstr_release(ret);
1583             return hres;
1584         }
1585
1586         ret_str[i] = code;
1587     }
1588
1589     if(r)
1590         *r = jsval_string(ret);
1591     else
1592         jsstr_release(ret);
1593     return S_OK;
1594 }
1595
1596 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1597         jsval_t *r)
1598 {
1599     HRESULT hres;
1600
1601     TRACE("\n");
1602
1603     switch(flags) {
1604     case INVOKE_FUNC: {
1605         jsstr_t *str;
1606
1607         if(argc) {
1608             hres = to_string(ctx, argv[0], &str);
1609             if(FAILED(hres))
1610                 return hres;
1611         }else {
1612             str = jsstr_empty();
1613         }
1614
1615         *r = jsval_string(str);
1616         break;
1617     }
1618     case DISPATCH_CONSTRUCT: {
1619         jsdisp_t *ret;
1620
1621         if(argc) {
1622             jsstr_t *str;
1623
1624             hres = to_string(ctx, argv[0], &str);
1625             if(FAILED(hres))
1626                 return hres;
1627
1628             hres = create_string(ctx, str, &ret);
1629             jsstr_release(str);
1630         }else {
1631             hres = create_string(ctx, jsstr_empty(), &ret);
1632         }
1633
1634         if(FAILED(hres))
1635             return hres;
1636
1637         *r = jsval_obj(ret);
1638         break;
1639     }
1640
1641     default:
1642         FIXME("unimplemented flags: %x\n", flags);
1643         return E_NOTIMPL;
1644     }
1645
1646     return S_OK;
1647 }
1648
1649 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1650 {
1651     StringInstance *string;
1652     HRESULT hres;
1653
1654     string = heap_alloc_zero(sizeof(StringInstance));
1655     if(!string)
1656         return E_OUTOFMEMORY;
1657
1658     if(object_prototype)
1659         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1660     else
1661         hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1662     if(FAILED(hres)) {
1663         heap_free(string);
1664         return hres;
1665     }
1666
1667     string->str = jsstr_addref(str);
1668     *ret = string;
1669     return S_OK;
1670 }
1671
1672 static const builtin_prop_t StringConstr_props[] = {
1673     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1674 };
1675
1676 static const builtin_info_t StringConstr_info = {
1677     JSCLASS_FUNCTION,
1678     {NULL, Function_value, 0},
1679     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1680     StringConstr_props,
1681     NULL,
1682     NULL
1683 };
1684
1685 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1686 {
1687     StringInstance *string;
1688     HRESULT hres;
1689
1690     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1691
1692     hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1693     if(FAILED(hres))
1694         return hres;
1695
1696     hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1697             PROPF_CONSTR|1, &string->dispex, ret);
1698
1699     jsdisp_release(&string->dispex);
1700     return hres;
1701 }
1702
1703 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1704 {
1705     StringInstance *string;
1706     HRESULT hres;
1707
1708     hres = string_alloc(ctx, NULL, str, &string);
1709     if(FAILED(hres))
1710         return hres;
1711
1712     *ret = &string->dispex;
1713     return S_OK;
1714
1715 }