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