jscript: Added new variable representation and use it for internal function return...
[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, VARIANT *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, VARIANT *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, VARIANT *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, VARIANT *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(ctx, argv, 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, VARIANT *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, VARIANT *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, VARIANT *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, VARIANT *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, VARIANT *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, 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, VARIANT *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, 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, VARIANT *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(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, VARIANT *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, VARIANT *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, VARIANT *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, VARIANT *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(ctx, argv, 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, VARIANT *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, VARIANT *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(ctx, argv, 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, VARIANT *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, VARIANT *argv,
588         jsval_t *r, jsexcept_t *ei)
589 {
590     const WCHAR *str;
591     jsdisp_t *regexp;
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     switch(V_VT(argv)) {
605     case VT_DISPATCH:
606         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(argv));
607         if(regexp) {
608             if(is_class(regexp, JSCLASS_REGEXP))
609                 break;
610             jsdisp_release(regexp);
611         }
612     default: {
613         BSTR match_str;
614
615         hres = to_string(ctx, argv, 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
626     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
627     if(SUCCEEDED(hres)) {
628         if(!val_str)
629             val_str = SysAllocStringLen(str, length);
630         if(val_str)
631             hres = regexp_string_match(ctx, regexp, val_str, r, ei);
632         else
633             hres = E_OUTOFMEMORY;
634     }
635
636     jsdisp_release(regexp);
637     SysFreeString(val_str);
638     return hres;
639 }
640
641 typedef struct {
642     WCHAR *buf;
643     DWORD size;
644     DWORD len;
645 } strbuf_t;
646
647 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
648 {
649     if(!len)
650         return S_OK;
651
652     if(len + buf->len > buf->size) {
653         WCHAR *new_buf;
654         DWORD new_size;
655
656         new_size = buf->size ? buf->size<<1 : 16;
657         if(new_size < buf->len+len)
658             new_size = buf->len+len;
659         if(buf->buf)
660             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
661         else
662             new_buf = heap_alloc(new_size*sizeof(WCHAR));
663         if(!new_buf)
664             return E_OUTOFMEMORY;
665
666         buf->buf = new_buf;
667         buf->size = new_size;
668     }
669
670     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
671     buf->len += len;
672     return S_OK;
673 }
674
675 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func, const WCHAR *str, match_result_t *match,
676         match_result_t *parens, DWORD parens_cnt, BSTR *ret, jsexcept_t *ei)
677 {
678     VARIANT *argv;
679     unsigned argc;
680     jsval_t val;
681     DWORD i;
682     HRESULT hres = S_OK;
683
684     argc = parens_cnt+3;
685     argv = heap_alloc_zero(sizeof(VARIANT)*argc);
686     if(!argv)
687         return E_OUTOFMEMORY;
688
689     V_VT(argv) = VT_BSTR;
690     V_BSTR(argv) = SysAllocStringLen(match->str, match->len);
691     if(!V_BSTR(argv))
692         hres = E_OUTOFMEMORY;
693
694     if(SUCCEEDED(hres)) {
695         for(i=0; i < parens_cnt; i++) {
696             V_VT(argv+i+1) = VT_BSTR;
697             V_BSTR(argv+i+1) = SysAllocStringLen(parens[i].str, parens[i].len);
698             if(!V_BSTR(argv+i+1)) {
699                hres = E_OUTOFMEMORY;
700                break;
701             }
702         }
703     }
704
705     if(SUCCEEDED(hres)) {
706         num_set_int(argv+parens_cnt+1, match->str - str);
707
708         V_VT(argv+parens_cnt+2) = VT_BSTR;
709         V_BSTR(argv+parens_cnt+2) = SysAllocString(str);
710         if(!V_BSTR(argv+parens_cnt+2))
711             hres = E_OUTOFMEMORY;
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(V_BSTR(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, VARIANT *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     switch(V_VT(argv)) {
764     case VT_DISPATCH:
765         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(argv));
766         if(regexp) {
767             if(is_class(regexp, JSCLASS_REGEXP)) {
768                 break;
769             }else {
770                 jsdisp_release(regexp);
771                 regexp = NULL;
772             }
773         }
774
775     default:
776         hres = to_string(ctx, argv, ei, &match_str);
777         if(FAILED(hres)) {
778             SysFreeString(val_str);
779             return hres;
780         }
781     }
782
783     if(argc >= 2) {
784         switch(V_VT(argv+1)) {
785         case VT_DISPATCH:
786             rep_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(argv+1));
787             if(rep_func) {
788                 if(is_class(rep_func, JSCLASS_FUNCTION)) {
789                     break;
790                 }else {
791                     jsdisp_release(rep_func);
792                     rep_func = NULL;
793                 }
794             }
795
796         default:
797             hres = to_string(ctx, argv+1, ei, &rep_str);
798             if(FAILED(hres))
799                 break;
800
801             rep_len = SysStringLen(rep_str);
802             if(!strchrW(rep_str, '$'))
803                 parens_ptr = NULL;
804         }
805     }
806
807     if(SUCCEEDED(hres)) {
808         const WCHAR *cp, *ecp;
809
810         cp = ecp = str;
811
812         while(1) {
813             if(regexp) {
814                 hres = regexp_match_next(ctx, regexp, re_flags, str, length, &cp, parens_ptr,
815                         &parens_size, &parens_cnt, &match);
816                 re_flags |= REM_CHECK_GLOBAL;
817
818                 if(hres == S_FALSE) {
819                     hres = S_OK;
820                     break;
821                 }
822                 if(FAILED(hres))
823                     break;
824
825                 if(!match.len)
826                     cp++;
827             }else {
828                 match.str = strstrW(cp, match_str);
829                 if(!match.str)
830                     break;
831                 match.len = SysStringLen(match_str);
832                 cp = match.str+match.len;
833             }
834
835             hres = strbuf_append(&ret, ecp, match.str-ecp);
836             ecp = match.str+match.len;
837             if(FAILED(hres))
838                 break;
839
840             if(rep_func) {
841                 BSTR cstr;
842
843                 hres = rep_call(ctx, rep_func, str, &match, parens, parens_cnt, &cstr, ei);
844                 if(FAILED(hres))
845                     break;
846
847                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
848                 SysFreeString(cstr);
849                 if(FAILED(hres))
850                     break;
851             }else if(rep_str && regexp) {
852                 const WCHAR *ptr = rep_str, *ptr2;
853
854                 while((ptr2 = strchrW(ptr, '$'))) {
855                     hres = strbuf_append(&ret, ptr, ptr2-ptr);
856                     if(FAILED(hres))
857                         break;
858
859                     switch(ptr2[1]) {
860                     case '$':
861                         hres = strbuf_append(&ret, ptr2, 1);
862                         ptr = ptr2+2;
863                         break;
864                     case '&':
865                         hres = strbuf_append(&ret, match.str, match.len);
866                         ptr = ptr2+2;
867                         break;
868                     case '`':
869                         hres = strbuf_append(&ret, str, match.str-str);
870                         ptr = ptr2+2;
871                         break;
872                     case '\'':
873                         hres = strbuf_append(&ret, ecp, (str+length)-ecp);
874                         ptr = ptr2+2;
875                         break;
876                     default: {
877                         DWORD idx;
878
879                         if(!isdigitW(ptr2[1])) {
880                             hres = strbuf_append(&ret, ptr2, 1);
881                             ptr = ptr2+1;
882                             break;
883                         }
884
885                         idx = ptr2[1] - '0';
886                         if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= parens_cnt) {
887                             idx = idx*10 + (ptr[2]-'0');
888                             ptr = ptr2+3;
889                         }else if(idx && idx <= parens_cnt) {
890                             ptr = ptr2+2;
891                         }else {
892                             hres = strbuf_append(&ret, ptr2, 1);
893                             ptr = ptr2+1;
894                             break;
895                         }
896
897                         hres = strbuf_append(&ret, parens[idx-1].str, parens[idx-1].len);
898                     }
899                     }
900
901                     if(FAILED(hres))
902                         break;
903                 }
904
905                 if(SUCCEEDED(hres))
906                     hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
907                 if(FAILED(hres))
908                     break;
909             }else if(rep_str) {
910                 hres = strbuf_append(&ret, rep_str, rep_len);
911                 if(FAILED(hres))
912                     break;
913             }else {
914                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
915
916                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
917                 if(FAILED(hres))
918                     break;
919             }
920
921             if(!regexp)
922                 break;
923         }
924
925         if(SUCCEEDED(hres))
926             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
927     }
928
929     if(rep_func)
930         jsdisp_release(rep_func);
931     SysFreeString(rep_str);
932     SysFreeString(match_str);
933     heap_free(parens);
934
935     if(SUCCEEDED(hres) && match.str && regexp) {
936         if(!val_str)
937             val_str = SysAllocStringLen(str, length);
938         if(val_str) {
939             SysFreeString(ctx->last_match);
940             ctx->last_match = val_str;
941             val_str = NULL;
942             ctx->last_match_index = match.str-str;
943             ctx->last_match_length = match.len;
944         }else {
945             hres = E_OUTOFMEMORY;
946         }
947     }
948
949     if(regexp)
950         jsdisp_release(regexp);
951     SysFreeString(val_str);
952
953     if(SUCCEEDED(hres) && r) {
954         ret_str = SysAllocStringLen(ret.buf, ret.len);
955         if(!ret_str)
956             return E_OUTOFMEMORY;
957
958         TRACE("= %s\n", debugstr_w(ret_str));
959         *r = jsval_string(ret_str);
960     }
961
962     heap_free(ret.buf);
963     return hres;
964 }
965
966 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
967         jsval_t *r, jsexcept_t *ei)
968 {
969     jsdisp_t *regexp = NULL;
970     const WCHAR *str, *cp;
971     match_result_t match;
972     DWORD length;
973     BSTR val_str;
974     HRESULT hres;
975
976     TRACE("\n");
977
978     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
979     if(FAILED(hres))
980         return hres;
981
982     if(!argc) {
983         if(r)
984             *r = jsval_null();
985         SysFreeString(val_str);
986         return S_OK;
987     }
988
989     if(V_VT(argv) == VT_DISPATCH) {
990         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(argv));
991         if(regexp) {
992             if(!is_class(regexp, JSCLASS_REGEXP)) {
993                 jsdisp_release(regexp);
994                 regexp = NULL;
995             }
996         }
997     }
998
999     if(!regexp) {
1000         hres = create_regexp_var(ctx, argv, NULL, &regexp);
1001         if(FAILED(hres)) {
1002             SysFreeString(val_str);
1003             return hres;
1004         }
1005     }
1006
1007     cp = str;
1008     hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX, str, length, &cp, NULL, NULL, NULL, &match);
1009     SysFreeString(val_str);
1010     jsdisp_release(regexp);
1011     if(FAILED(hres))
1012         return hres;
1013
1014     if(r)
1015         *r = jsval_number(hres == S_OK ? match.str-str : -1);
1016     return S_OK;
1017 }
1018
1019 /* ECMA-262 3rd Edition    15.5.4.13 */
1020 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1021         jsval_t *r, jsexcept_t *ei)
1022 {
1023     const WCHAR *str;
1024     BSTR val_str;
1025     DWORD length;
1026     INT start=0, end;
1027     double d;
1028     HRESULT hres;
1029
1030     TRACE("\n");
1031
1032     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1033     if(FAILED(hres))
1034         return hres;
1035
1036     if(argc) {
1037         hres = to_integer(ctx, argv, ei, &d);
1038         if(FAILED(hres)) {
1039             SysFreeString(val_str);
1040             return hres;
1041         }
1042
1043         if(is_int32(d)) {
1044             start = d;
1045             if(start < 0) {
1046                 start = length + start;
1047                 if(start < 0)
1048                     start = 0;
1049             }else if(start > length) {
1050                 start = length;
1051             }
1052         }else if(d > 0) {
1053             start = length;
1054         }
1055     }
1056
1057     if(argc >= 2) {
1058         hres = to_integer(ctx, argv+1, ei, &d);
1059         if(FAILED(hres)) {
1060             SysFreeString(val_str);
1061             return hres;
1062         }
1063
1064         if(is_int32(d)) {
1065             end = d;
1066             if(end < 0) {
1067                 end = length + end;
1068                 if(end < 0)
1069                     end = 0;
1070             }else if(end > length) {
1071                 end = length;
1072             }
1073         }else {
1074             end = d < 0.0 ? 0 : length;
1075         }
1076     }else {
1077         end = length;
1078     }
1079
1080     if(end < start)
1081         end = start;
1082
1083     if(r) {
1084         BSTR retstr = SysAllocStringLen(str+start, end-start);
1085         if(!retstr) {
1086             SysFreeString(val_str);
1087             return E_OUTOFMEMORY;
1088         }
1089
1090         *r = jsval_string(retstr);
1091     }
1092
1093     SysFreeString(val_str);
1094     return S_OK;
1095 }
1096
1097 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1098         jsval_t *r, jsexcept_t *ei)
1099 {
1100     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1101     return do_attributeless_tag_format(ctx, jsthis, r, ei, smalltagW);
1102 }
1103
1104 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1105         jsval_t *r, jsexcept_t *ei)
1106 {
1107     match_result_t *match_result = NULL;
1108     DWORD length, match_cnt, i, match_len = 0;
1109     const WCHAR *str, *ptr, *ptr2;
1110     BOOL use_regexp = FALSE;
1111     VARIANT var;
1112     jsdisp_t *array;
1113     BSTR val_str, match_str = NULL;
1114     HRESULT hres;
1115
1116     TRACE("\n");
1117
1118     if(argc != 1) {
1119         FIXME("unsupported args\n");
1120         return E_NOTIMPL;
1121     }
1122
1123     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1124     if(FAILED(hres))
1125         return hres;
1126
1127     switch(V_VT(argv)) {
1128     case VT_DISPATCH: {
1129         jsdisp_t *regexp;
1130
1131         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(argv));
1132         if(regexp) {
1133             if(is_class(regexp, JSCLASS_REGEXP)) {
1134                 use_regexp = TRUE;
1135                 hres = regexp_match(ctx, regexp, str, length, TRUE, &match_result, &match_cnt);
1136                 jsdisp_release(regexp);
1137                 if(FAILED(hres)) {
1138                     SysFreeString(val_str);
1139                     return hres;
1140                 }
1141                 break;
1142             }
1143             jsdisp_release(regexp);
1144         }
1145     }
1146     default:
1147         hres = to_string(ctx, argv, ei, &match_str);
1148         if(FAILED(hres)) {
1149             SysFreeString(val_str);
1150             return hres;
1151         }
1152
1153         match_len = SysStringLen(match_str);
1154         if(!match_len) {
1155             SysFreeString(match_str);
1156             match_str = NULL;
1157         }
1158     }
1159
1160     hres = create_array(ctx, 0, &array);
1161
1162     if(SUCCEEDED(hres)) {
1163         ptr = str;
1164         for(i=0;; i++) {
1165             if(use_regexp) {
1166                 if(i == match_cnt)
1167                     break;
1168                 ptr2 = match_result[i].str;
1169             }else if(match_str) {
1170                 ptr2 = strstrW(ptr, match_str);
1171                 if(!ptr2)
1172                     break;
1173             }else {
1174                 if(!*ptr)
1175                     break;
1176                 ptr2 = ptr+1;
1177             }
1178
1179             V_VT(&var) = VT_BSTR;
1180             V_BSTR(&var) = SysAllocStringLen(ptr, ptr2-ptr);
1181             if(!V_BSTR(&var)) {
1182                 hres = E_OUTOFMEMORY;
1183                 break;
1184             }
1185
1186             hres = jsdisp_propput_idx(array, i, &var, ei);
1187             SysFreeString(V_BSTR(&var));
1188             if(FAILED(hres))
1189                 break;
1190
1191             if(use_regexp)
1192                 ptr = match_result[i].str + match_result[i].len;
1193             else if(match_str)
1194                 ptr = ptr2 + match_len;
1195             else
1196                 ptr++;
1197         }
1198     }
1199
1200     if(SUCCEEDED(hres) && (match_str || use_regexp)) {
1201         DWORD len = (str+length) - ptr;
1202
1203         if(len || match_str) {
1204             V_VT(&var) = VT_BSTR;
1205             V_BSTR(&var) = SysAllocStringLen(ptr, len);
1206
1207             if(V_BSTR(&var)) {
1208                 hres = jsdisp_propput_idx(array, i, &var, ei);
1209                 SysFreeString(V_BSTR(&var));
1210             }else {
1211                 hres = E_OUTOFMEMORY;
1212             }
1213         }
1214     }
1215
1216     SysFreeString(match_str);
1217     SysFreeString(val_str);
1218     heap_free(match_result);
1219
1220     if(SUCCEEDED(hres) && r)
1221         *r = jsval_obj(array);
1222     else
1223         jsdisp_release(array);
1224
1225     return hres;
1226 }
1227
1228 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1229         jsval_t *r, jsexcept_t *ei)
1230 {
1231     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1232     return do_attributeless_tag_format(ctx, jsthis, r, ei, striketagW);
1233 }
1234
1235 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1236         jsval_t *r, jsexcept_t *ei)
1237 {
1238     static const WCHAR subtagW[] = {'S','U','B',0};
1239     return do_attributeless_tag_format(ctx, jsthis, r, ei, subtagW);
1240 }
1241
1242 /* ECMA-262 3rd Edition    15.5.4.15 */
1243 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1244         jsval_t *r, jsexcept_t *ei)
1245 {
1246     const WCHAR *str;
1247     BSTR val_str;
1248     INT start=0, end;
1249     DWORD length;
1250     double d;
1251     HRESULT hres;
1252
1253     TRACE("\n");
1254
1255     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1256     if(FAILED(hres))
1257         return hres;
1258
1259     if(argc >= 1) {
1260         hres = to_integer(ctx, argv, ei, &d);
1261         if(FAILED(hres)) {
1262             SysFreeString(val_str);
1263             return hres;
1264         }
1265
1266         if(d >= 0)
1267             start = is_int32(d) ? min(length, d) : length;
1268     }
1269
1270     if(argc >= 2) {
1271         hres = to_integer(ctx, argv+1, ei, &d);
1272         if(FAILED(hres)) {
1273             SysFreeString(val_str);
1274             return hres;
1275         }
1276
1277         if(d >= 0)
1278             end = is_int32(d) ? min(length, d) : length;
1279         else
1280             end = 0;
1281     }else {
1282         end = length;
1283     }
1284
1285     if(start > end) {
1286         INT tmp = start;
1287         start = end;
1288         end = tmp;
1289     }
1290
1291     if(r) {
1292         BSTR ret = SysAllocStringLen(str+start, end-start);
1293         if(!ret) {
1294             SysFreeString(val_str);
1295             return E_OUTOFMEMORY;
1296         }
1297         *r = jsval_string(ret);
1298     }
1299     SysFreeString(val_str);
1300     return S_OK;
1301 }
1302
1303 /* ECMA-262 3rd Edition    B.2.3 */
1304 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1305         jsval_t *r, jsexcept_t *ei)
1306 {
1307     BSTR val_str;
1308     const WCHAR *str;
1309     INT start=0, len;
1310     DWORD length;
1311     double d;
1312     HRESULT hres;
1313
1314     TRACE("\n");
1315
1316     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1317     if(FAILED(hres))
1318         return hres;
1319
1320     if(argc >= 1) {
1321         hres = to_integer(ctx, argv, ei, &d);
1322         if(FAILED(hres)) {
1323             SysFreeString(val_str);
1324             return hres;
1325         }
1326
1327         if(d >= 0)
1328             start = is_int32(d) ? min(length, d) : length;
1329     }
1330
1331     if(argc >= 2) {
1332         hres = to_integer(ctx, argv+1, ei, &d);
1333         if(FAILED(hres)) {
1334             SysFreeString(val_str);
1335             return hres;
1336         }
1337
1338         if(d >= 0.0)
1339             len = is_int32(d) ? min(length-start, d) : length-start;
1340         else
1341             len = 0;
1342     }else {
1343         len = length-start;
1344     }
1345
1346     hres = S_OK;
1347     if(r) {
1348         BSTR ret = SysAllocStringLen(str+start, len);
1349         if(ret)
1350             *r = jsval_string(ret);
1351         else
1352             hres = E_OUTOFMEMORY;
1353     }
1354
1355     SysFreeString(val_str);
1356     return hres;
1357 }
1358
1359 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1360         jsval_t *r, jsexcept_t *ei)
1361 {
1362     static const WCHAR suptagW[] = {'S','U','P',0};
1363     return do_attributeless_tag_format(ctx, jsthis, r, ei, suptagW);
1364 }
1365
1366 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1367         jsval_t *r, jsexcept_t *ei)
1368 {
1369     const WCHAR* str;
1370     DWORD length;
1371     BSTR val_str;
1372     HRESULT  hres;
1373
1374     TRACE("\n");
1375
1376     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1377     if(FAILED(hres))
1378         return hres;
1379
1380     if(r) {
1381         if(!val_str) {
1382             val_str = SysAllocStringLen(str, length);
1383             if(!val_str)
1384                 return E_OUTOFMEMORY;
1385         }
1386
1387         strlwrW(val_str);
1388         *r = jsval_string(val_str);
1389     }
1390     else SysFreeString(val_str);
1391     return S_OK;
1392 }
1393
1394 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1395         jsval_t *r, jsexcept_t *ei)
1396 {
1397     const WCHAR* str;
1398     DWORD length;
1399     BSTR val_str;
1400     HRESULT hres;
1401
1402     TRACE("\n");
1403
1404     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1405     if(FAILED(hres))
1406         return hres;
1407
1408     if(r) {
1409         if(!val_str) {
1410             val_str = SysAllocStringLen(str, length);
1411             if(!val_str)
1412                 return E_OUTOFMEMORY;
1413         }
1414
1415         struprW(val_str);
1416         *r = jsval_string(val_str);
1417     }
1418     else SysFreeString(val_str);
1419     return S_OK;
1420 }
1421
1422 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1423         jsval_t *r, jsexcept_t *ei)
1424 {
1425     FIXME("\n");
1426     return E_NOTIMPL;
1427 }
1428
1429 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1430         jsval_t *r, jsexcept_t *ei)
1431 {
1432     FIXME("\n");
1433     return E_NOTIMPL;
1434 }
1435
1436 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1437         jsval_t *r, jsexcept_t *ei)
1438 {
1439     FIXME("\n");
1440     return E_NOTIMPL;
1441 }
1442
1443 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1444         jsval_t *r, jsexcept_t *ei)
1445 {
1446     StringInstance *This = string_from_vdisp(jsthis);
1447
1448     TRACE("\n");
1449
1450     switch(flags) {
1451     case INVOKE_FUNC:
1452         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
1453     case DISPATCH_PROPERTYGET: {
1454         BSTR str = SysAllocString(This->str);
1455         if(!str)
1456             return E_OUTOFMEMORY;
1457
1458         *r = jsval_string(str);
1459         break;
1460     }
1461     default:
1462         FIXME("flags %x\n", flags);
1463         return E_NOTIMPL;
1464     }
1465
1466     return S_OK;
1467 }
1468
1469 static void String_destructor(jsdisp_t *dispex)
1470 {
1471     StringInstance *This = (StringInstance*)dispex;
1472
1473     heap_free(This->str);
1474     heap_free(This);
1475 }
1476
1477 static const builtin_prop_t String_props[] = {
1478     {anchorW,                String_anchor,                PROPF_METHOD|1},
1479     {bigW,                   String_big,                   PROPF_METHOD},
1480     {blinkW,                 String_blink,                 PROPF_METHOD},
1481     {boldW,                  String_bold,                  PROPF_METHOD},
1482     {charAtW,                String_charAt,                PROPF_METHOD|1},
1483     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1484     {concatW,                String_concat,                PROPF_METHOD|1},
1485     {fixedW,                 String_fixed,                 PROPF_METHOD},
1486     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1487     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1488     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1489     {italicsW,               String_italics,               PROPF_METHOD},
1490     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1491     {lengthW,                String_length,                0},
1492     {linkW,                  String_link,                  PROPF_METHOD|1},
1493     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1494     {matchW,                 String_match,                 PROPF_METHOD|1},
1495     {replaceW,               String_replace,               PROPF_METHOD|1},
1496     {searchW,                String_search,                PROPF_METHOD},
1497     {sliceW,                 String_slice,                 PROPF_METHOD},
1498     {smallW,                 String_small,                 PROPF_METHOD},
1499     {splitW,                 String_split,                 PROPF_METHOD|2},
1500     {strikeW,                String_strike,                PROPF_METHOD},
1501     {subW,                   String_sub,                   PROPF_METHOD},
1502     {substrW,                String_substr,                PROPF_METHOD|2},
1503     {substringW,             String_substring,             PROPF_METHOD|2},
1504     {supW,                   String_sup,                   PROPF_METHOD},
1505     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1506     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1507     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1508     {toStringW,              String_toString,              PROPF_METHOD},
1509     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1510     {valueOfW,               String_valueOf,               PROPF_METHOD}
1511 };
1512
1513 static const builtin_info_t String_info = {
1514     JSCLASS_STRING,
1515     {NULL, String_value, 0},
1516     sizeof(String_props)/sizeof(*String_props),
1517     String_props,
1518     String_destructor,
1519     NULL
1520 };
1521
1522 static const builtin_prop_t StringInst_props[] = {
1523     {lengthW,                String_length,                0}
1524 };
1525
1526 static const builtin_info_t StringInst_info = {
1527     JSCLASS_STRING,
1528     {NULL, String_value, 0},
1529     sizeof(StringInst_props)/sizeof(*StringInst_props),
1530     StringInst_props,
1531     String_destructor,
1532     NULL
1533 };
1534
1535 /* ECMA-262 3rd Edition    15.5.3.2 */
1536 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1537         unsigned argc, VARIANT *argv, jsval_t *r, jsexcept_t *ei)
1538 {
1539     DWORD i, code;
1540     BSTR ret;
1541     HRESULT hres;
1542
1543     TRACE("\n");
1544
1545     ret = SysAllocStringLen(NULL, argc);
1546     if(!ret)
1547         return E_OUTOFMEMORY;
1548
1549     for(i=0; i<argc; i++) {
1550         hres = to_uint32(ctx, argv+i, ei, &code);
1551         if(FAILED(hres)) {
1552             SysFreeString(ret);
1553             return hres;
1554         }
1555
1556         ret[i] = code;
1557     }
1558
1559     if(r)
1560         *r = jsval_string(ret);
1561     else
1562         SysFreeString(ret);
1563     return S_OK;
1564 }
1565
1566 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, VARIANT *argv,
1567         jsval_t *r, jsexcept_t *ei)
1568 {
1569     HRESULT hres;
1570
1571     TRACE("\n");
1572
1573     switch(flags) {
1574     case INVOKE_FUNC: {
1575         BSTR str;
1576
1577         if(argc) {
1578             hres = to_string(ctx, argv, ei, &str);
1579             if(FAILED(hres))
1580                 return hres;
1581         }else {
1582             str = SysAllocStringLen(NULL, 0);
1583             if(!str)
1584                 return E_OUTOFMEMORY;
1585         }
1586
1587         *r = jsval_string(str);
1588         break;
1589     }
1590     case DISPATCH_CONSTRUCT: {
1591         jsdisp_t *ret;
1592
1593         if(argc) {
1594             BSTR str;
1595
1596             hres = to_string(ctx, argv, ei, &str);
1597             if(FAILED(hres))
1598                 return hres;
1599
1600             hres = create_string(ctx, str, SysStringLen(str), &ret);
1601             SysFreeString(str);
1602         }else {
1603             hres = create_string(ctx, NULL, 0, &ret);
1604         }
1605
1606         if(FAILED(hres))
1607             return hres;
1608
1609         *r = jsval_obj(ret);
1610         break;
1611     }
1612
1613     default:
1614         FIXME("unimplemented flags: %x\n", flags);
1615         return E_NOTIMPL;
1616     }
1617
1618     return S_OK;
1619 }
1620
1621 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, StringInstance **ret)
1622 {
1623     StringInstance *string;
1624     HRESULT hres;
1625
1626     string = heap_alloc_zero(sizeof(StringInstance));
1627     if(!string)
1628         return E_OUTOFMEMORY;
1629
1630     if(object_prototype)
1631         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1632     else
1633         hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1634     if(FAILED(hres)) {
1635         heap_free(string);
1636         return hres;
1637     }
1638
1639     *ret = string;
1640     return S_OK;
1641 }
1642
1643 static const builtin_prop_t StringConstr_props[] = {
1644     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1645 };
1646
1647 static const builtin_info_t StringConstr_info = {
1648     JSCLASS_FUNCTION,
1649     {NULL, Function_value, 0},
1650     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1651     StringConstr_props,
1652     NULL,
1653     NULL
1654 };
1655
1656 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1657 {
1658     StringInstance *string;
1659     HRESULT hres;
1660
1661     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1662
1663     hres = string_alloc(ctx, object_prototype, &string);
1664     if(FAILED(hres))
1665         return hres;
1666
1667     hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1668             PROPF_CONSTR|1, &string->dispex, ret);
1669
1670     jsdisp_release(&string->dispex);
1671     return hres;
1672 }
1673
1674 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, jsdisp_t **ret)
1675 {
1676     StringInstance *string;
1677     HRESULT hres;
1678
1679     hres = string_alloc(ctx, NULL, &string);
1680     if(FAILED(hres))
1681         return hres;
1682
1683     if(len == -1)
1684         len = strlenW(str);
1685
1686     string->length = len;
1687     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1688     if(!string->str) {
1689         jsdisp_release(&string->dispex);
1690         return E_OUTOFMEMORY;
1691     }
1692
1693     memcpy(string->str, str, len*sizeof(WCHAR));
1694     string->str[len] = 0;
1695
1696     *ret = &string->dispex;
1697     return S_OK;
1698
1699 }