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