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