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