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