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