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