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