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