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