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