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