jscript: Added $ handling to String.replace.
[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 = NULL;
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, **parens_ptr = &parens;
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             rep_len = SysStringLen(rep_str);
888             if(!strchrW(rep_str, '$'))
889                 parens_ptr = NULL;
890         }
891     }
892
893     if(SUCCEEDED(hres)) {
894         const WCHAR *cp, *ecp;
895
896         cp = ecp = str;
897
898         while(1) {
899             if(regexp) {
900                 hres = regexp_match_next(regexp, gcheck, str, length, &cp, parens_ptr,
901                                          &parens_size, &parens_cnt, &match);
902                 gcheck = TRUE;
903
904                 if(hres == S_FALSE) {
905                     hres = S_OK;
906                     break;
907                 }
908                 if(FAILED(hres))
909                     break;
910             }else {
911                 match.str = strstrW(cp, match_str);
912                 if(!match.str)
913                     break;
914                 match.len = SysStringLen(match_str);
915                 cp = match.str+match.len;
916             }
917
918             hres = strbuf_append(&ret, ecp, match.str-ecp);
919             ecp = match.str+match.len;
920             if(FAILED(hres))
921                 break;
922
923             if(rep_func) {
924                 BSTR cstr;
925
926                 hres = rep_call(rep_func, str, &match, parens, parens_cnt, lcid, &cstr, ei, caller);
927                 if(FAILED(hres))
928                     break;
929
930                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
931                 SysFreeString(cstr);
932                 if(FAILED(hres))
933                     break;
934             }else if(rep_str && regexp) {
935                 const WCHAR *ptr = rep_str, *ptr2;
936
937                 while((ptr2 = strchrW(ptr, '$'))) {
938                     hres = strbuf_append(&ret, ptr, ptr2-ptr);
939                     if(FAILED(hres))
940                         break;
941
942                     switch(ptr2[1]) {
943                     case '$':
944                         hres = strbuf_append(&ret, ptr2, 1);
945                         ptr = ptr2+2;
946                         break;
947                     case '&':
948                         hres = strbuf_append(&ret, match.str, match.len);
949                         ptr = ptr2+2;
950                         break;
951                     case '`':
952                         hres = strbuf_append(&ret, str, match.str-str);
953                         ptr = ptr2+2;
954                         break;
955                     case '\'':
956                         hres = strbuf_append(&ret, ecp, (str+length)-ecp);
957                         ptr = ptr2+2;
958                         break;
959                     default: {
960                         DWORD idx;
961
962                         if(!isdigitW(ptr2[1])) {
963                             hres = strbuf_append(&ret, ptr2, 1);
964                             ptr = ptr2+1;
965                             break;
966                         }
967
968                         idx = ptr2[1] - '0';
969                         if(isdigitW(ptr[3]) && idx*10 + (ptr[2]-'0') <= parens_cnt) {
970                             idx = idx*10 + (ptr[2]-'0');
971                             ptr = ptr2+3;
972                         }else if(idx && idx <= parens_cnt) {
973                             ptr = ptr2+2;
974                         }else {
975                             hres = strbuf_append(&ret, ptr2, 1);
976                             ptr = ptr2+1;
977                             break;
978                         }
979
980                         hres = strbuf_append(&ret, parens[idx-1].str, parens[idx-1].len);
981                     }
982                     }
983
984                     if(FAILED(hres))
985                         break;
986                 }
987
988                 if(SUCCEEDED(hres))
989                     hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
990                 if(FAILED(hres))
991                     break;
992             }else if(rep_str) {
993                 hres = strbuf_append(&ret, rep_str, rep_len);
994                 if(FAILED(hres))
995                     break;
996             }else {
997                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
998
999                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
1000                 if(FAILED(hres))
1001                     break;
1002             }
1003         }
1004
1005         if(SUCCEEDED(hres))
1006             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
1007     }
1008
1009     if(rep_func)
1010         jsdisp_release(rep_func);
1011     if(regexp)
1012         jsdisp_release(regexp);
1013     SysFreeString(val_str);
1014     SysFreeString(rep_str);
1015     SysFreeString(match_str);
1016     heap_free(parens);
1017
1018     if(SUCCEEDED(hres) && retv) {
1019         ret_str = SysAllocStringLen(ret.buf, ret.len);
1020         if(!ret_str)
1021             return E_OUTOFMEMORY;
1022
1023         V_VT(retv) = VT_BSTR;
1024         V_BSTR(retv) = ret_str;
1025         TRACE("= %s\n", debugstr_w(ret_str));
1026     }
1027
1028     heap_free(ret.buf);
1029     return hres;
1030 }
1031
1032 static HRESULT String_search(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1033         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1034 {
1035     FIXME("\n");
1036     return E_NOTIMPL;
1037 }
1038
1039 /* ECMA-262 3rd Edition    15.5.4.13 */
1040 static HRESULT String_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1041         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1042 {
1043     const WCHAR *str;
1044     BSTR val_str = NULL;
1045     DWORD length;
1046     INT start=0, end;
1047     VARIANT v;
1048     HRESULT hres;
1049
1050     TRACE("\n");
1051
1052     if(!is_class(dispex, JSCLASS_STRING)) {
1053         VARIANT this;
1054
1055         V_VT(&this) = VT_DISPATCH;
1056         V_DISPATCH(&this) = (IDispatch*)_IDispatchEx_(dispex);
1057
1058         hres = to_string(dispex->ctx, &this, ei, &val_str);
1059         if(FAILED(hres))
1060             return hres;
1061
1062         str = val_str;
1063         length = SysStringLen(val_str);
1064     }
1065     else {
1066         StringInstance *this = (StringInstance*)dispex;
1067
1068         str = this->str;
1069         length = this->length;
1070     }
1071
1072     if(arg_cnt(dp)) {
1073         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
1074         if(FAILED(hres)) {
1075             SysFreeString(val_str);
1076             return hres;
1077         }
1078
1079         if(V_VT(&v) == VT_I4) {
1080             start = V_I4(&v);
1081             if(start < 0) {
1082                 start = length + start;
1083                 if(start < 0)
1084                     start = 0;
1085             }else if(start > length) {
1086                 start = length;
1087             }
1088         }else {
1089             start = V_R8(&v) < 0.0 ? 0 : length;
1090         }
1091     }else {
1092         start = 0;
1093     }
1094
1095     if(arg_cnt(dp) >= 2) {
1096         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
1097         if(FAILED(hres)) {
1098             SysFreeString(val_str);
1099             return hres;
1100         }
1101
1102         if(V_VT(&v) == VT_I4) {
1103             end = V_I4(&v);
1104             if(end < 0) {
1105                 end = length + end;
1106                 if(end < 0)
1107                     end = 0;
1108             }else if(end > length) {
1109                 end = length;
1110             }
1111         }else {
1112             end = V_R8(&v) < 0.0 ? 0 : length;
1113         }
1114     }else {
1115         end = length;
1116     }
1117
1118     if(end < start)
1119         end = start;
1120
1121     if(retv) {
1122         BSTR retstr = SysAllocStringLen(str+start, end-start);
1123         if(!retstr) {
1124             SysFreeString(val_str);
1125             return E_OUTOFMEMORY;
1126         }
1127
1128         V_VT(retv) = VT_BSTR;
1129         V_BSTR(retv) = retstr;
1130     }
1131
1132     SysFreeString(val_str);
1133     return S_OK;
1134 }
1135
1136 static HRESULT String_small(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1137         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1138 {
1139     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1140     return do_attributeless_tag_format(dispex, lcid, flags, dp, retv, ei, sp, smalltagW);
1141 }
1142
1143 static HRESULT String_split(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1144         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1145 {
1146     match_result_t *match_result = NULL;
1147     DWORD length, match_cnt, i, match_len = 0;
1148     const WCHAR *str, *ptr, *ptr2;
1149     VARIANT *arg, var;
1150     DispatchEx *array;
1151     BSTR val_str = NULL, match_str = NULL;
1152     HRESULT hres;
1153
1154     TRACE("\n");
1155
1156     if(arg_cnt(dp) != 1) {
1157         FIXME("unsupported args\n");
1158         return E_NOTIMPL;
1159     }
1160
1161     if(!is_class(dispex, JSCLASS_STRING)) {
1162         VARIANT this;
1163
1164         V_VT(&this) = VT_DISPATCH;
1165         V_DISPATCH(&this) = (IDispatch*)_IDispatchEx_(dispex);
1166
1167         hres = to_string(dispex->ctx, &this, ei, &val_str);
1168         if(FAILED(hres))
1169             return hres;
1170
1171         str = val_str;
1172         length = SysStringLen(val_str);
1173     }
1174     else {
1175         StringInstance *this = (StringInstance*)dispex;
1176
1177         str = this->str;
1178         length = this->length;
1179     }
1180
1181     arg = get_arg(dp, 0);
1182     switch(V_VT(arg)) {
1183     case VT_DISPATCH: {
1184         DispatchEx *regexp;
1185
1186         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
1187         if(regexp) {
1188             if(is_class(regexp, JSCLASS_REGEXP)) {
1189                 hres = regexp_match(regexp, str, length, TRUE, &match_result, &match_cnt);
1190                 jsdisp_release(regexp);
1191                 if(FAILED(hres)) {
1192                     SysFreeString(val_str);
1193                     return hres;
1194                 }
1195                 break;
1196             }
1197             jsdisp_release(regexp);
1198         }
1199     }
1200     default:
1201         hres = to_string(dispex->ctx, arg, ei, &match_str);
1202         if(FAILED(hres)) {
1203             SysFreeString(val_str);
1204             return hres;
1205         }
1206
1207         match_len = SysStringLen(match_str);
1208         if(!match_len) {
1209             SysFreeString(match_str);
1210             match_str = NULL;
1211         }
1212     }
1213
1214     hres = create_array(dispex->ctx, 0, &array);
1215
1216     if(SUCCEEDED(hres)) {
1217         ptr = str;
1218         for(i=0;; i++) {
1219             if(match_result) {
1220                 if(i == match_cnt)
1221                     break;
1222                 ptr2 = match_result[i].str;
1223             }else if(match_str) {
1224                 ptr2 = strstrW(ptr, match_str);
1225                 if(!ptr2)
1226                     break;
1227             }else {
1228                 if(!*ptr)
1229                     break;
1230                 ptr2 = ptr+1;
1231             }
1232
1233             V_VT(&var) = VT_BSTR;
1234             V_BSTR(&var) = SysAllocStringLen(ptr, ptr2-ptr);
1235             if(!V_BSTR(&var)) {
1236                 hres = E_OUTOFMEMORY;
1237                 break;
1238             }
1239
1240             hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
1241             SysFreeString(V_BSTR(&var));
1242             if(FAILED(hres))
1243                 break;
1244
1245             if(match_result)
1246                 ptr = match_result[i].str + match_result[i].len;
1247             else if(match_str)
1248                 ptr = ptr2 + match_len;
1249             else
1250                 ptr++;
1251         }
1252     }
1253
1254     if(SUCCEEDED(hres) && (match_str || match_result)) {
1255         DWORD len = (str+length) - ptr;
1256
1257         if(len || match_str) {
1258             V_VT(&var) = VT_BSTR;
1259             V_BSTR(&var) = SysAllocStringLen(ptr, len);
1260
1261             if(V_BSTR(&var)) {
1262                 hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
1263                 SysFreeString(V_BSTR(&var));
1264             }else {
1265                 hres = E_OUTOFMEMORY;
1266             }
1267         }
1268     }
1269
1270     SysFreeString(match_str);
1271     SysFreeString(val_str);
1272     heap_free(match_result);
1273
1274     if(SUCCEEDED(hres) && retv) {
1275         V_VT(retv) = VT_DISPATCH;
1276         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
1277     }else {
1278         jsdisp_release(array);
1279     }
1280
1281     return hres;
1282 }
1283
1284 static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1285         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1286 {
1287     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1288     return do_attributeless_tag_format(dispex, lcid, flags, dp, retv, ei, sp, striketagW);
1289 }
1290
1291 static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1292         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1293 {
1294     static const WCHAR subtagW[] = {'S','U','B',0};
1295     return do_attributeless_tag_format(dispex, lcid, flags, dp, retv, ei, sp, subtagW);
1296 }
1297
1298 /* ECMA-262 3rd Edition    15.5.4.15 */
1299 static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1300         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1301 {
1302     const WCHAR *str;
1303     BSTR val_str = NULL;
1304     INT start=0, end;
1305     DWORD length;
1306     VARIANT v;
1307     HRESULT hres;
1308
1309     TRACE("\n");
1310
1311     if(!is_class(dispex, JSCLASS_STRING)) {
1312         VARIANT this;
1313
1314         V_VT(&this) = VT_DISPATCH;
1315         V_DISPATCH(&this) = (IDispatch*)_IDispatchEx_(dispex);
1316
1317         hres = to_string(dispex->ctx, &this, ei, &val_str);
1318         if(FAILED(hres))
1319             return hres;
1320
1321         str = val_str;
1322         length = SysStringLen(val_str);
1323     }
1324     else {
1325         StringInstance *this = (StringInstance*)dispex;
1326
1327         str = this->str;
1328         length = this->length;
1329     }
1330
1331     if(arg_cnt(dp) >= 1) {
1332         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
1333         if(FAILED(hres)) {
1334             SysFreeString(val_str);
1335             return hres;
1336         }
1337
1338         if(V_VT(&v) == VT_I4) {
1339             start = V_I4(&v);
1340             if(start < 0)
1341                 start = 0;
1342             else if(start >= length)
1343                 start = length;
1344         }else {
1345             start = V_R8(&v) < 0.0 ? 0 : length;
1346         }
1347     }
1348
1349     if(arg_cnt(dp) >= 2) {
1350         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
1351         if(FAILED(hres)) {
1352             SysFreeString(val_str);
1353             return hres;
1354         }
1355
1356         if(V_VT(&v) == VT_I4) {
1357             end = V_I4(&v);
1358             if(end < 0)
1359                 end = 0;
1360             else if(end > length)
1361                 end = length;
1362         }else {
1363             end = V_R8(&v) < 0.0 ? 0 : length;
1364         }
1365     }else {
1366         end = length;
1367     }
1368
1369     if(start > end) {
1370         INT tmp = start;
1371         start = end;
1372         end = tmp;
1373     }
1374
1375     if(retv) {
1376         V_VT(retv) = VT_BSTR;
1377         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
1378         if(!V_BSTR(retv)) {
1379             SysFreeString(val_str);
1380             return E_OUTOFMEMORY;
1381         }
1382     }
1383     SysFreeString(val_str);
1384     return S_OK;
1385 }
1386
1387 static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1388         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1389 {
1390     FIXME("\n");
1391     return E_NOTIMPL;
1392 }
1393
1394 static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1395         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1396 {
1397     static const WCHAR suptagW[] = {'S','U','P',0};
1398     return do_attributeless_tag_format(dispex, lcid, flags, dp, retv, ei, sp, suptagW);
1399 }
1400
1401 static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1402         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1403 {
1404     const WCHAR* str;
1405     DWORD length;
1406     BSTR val_str = NULL;
1407     HRESULT  hres;
1408
1409     TRACE("\n");
1410
1411     if(!is_class(dispex, JSCLASS_STRING)) {
1412         VARIANT this;
1413
1414         V_VT(&this) = VT_DISPATCH;
1415         V_DISPATCH(&this) = (IDispatch*)_IDispatchEx_(dispex);
1416
1417         hres = to_string(dispex->ctx, &this, ei, &val_str);
1418         if(FAILED(hres))
1419             return hres;
1420
1421         str = val_str;
1422         length = SysStringLen(val_str);
1423     }
1424     else {
1425         StringInstance *this = (StringInstance*)dispex;
1426
1427         str = this->str;
1428         length = this->length;
1429     }
1430
1431     if(retv) {
1432         if(!val_str) {
1433             val_str = SysAllocStringLen(str, length);
1434             if(!val_str)
1435                 return E_OUTOFMEMORY;
1436         }
1437
1438         strlwrW(val_str);
1439
1440         V_VT(retv) = VT_BSTR;
1441         V_BSTR(retv) = val_str;
1442     }
1443     else SysFreeString(val_str);
1444     return S_OK;
1445 }
1446
1447 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1448         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1449 {
1450     const WCHAR* str;
1451     DWORD length;
1452     BSTR val_str = NULL;
1453     HRESULT hres;
1454
1455     TRACE("\n");
1456
1457     if(!is_class(dispex, JSCLASS_STRING)) {
1458         VARIANT this;
1459
1460         V_VT(&this) = VT_DISPATCH;
1461         V_DISPATCH(&this) = (IDispatch*)_IDispatchEx_(dispex);
1462
1463         hres = to_string(dispex->ctx, &this, ei, &val_str);
1464         if(FAILED(hres))
1465             return hres;
1466
1467         str = val_str;
1468         length = SysStringLen(val_str);
1469     }
1470     else {
1471         StringInstance *this = (StringInstance*)dispex;
1472
1473         str = this->str;
1474         length = this->length;
1475     }
1476
1477     if(retv) {
1478         if(!val_str) {
1479             val_str = SysAllocStringLen(str, length);
1480             if(!val_str)
1481                 return E_OUTOFMEMORY;
1482         }
1483
1484         struprW(val_str);
1485
1486         V_VT(retv) = VT_BSTR;
1487         V_BSTR(retv) = val_str;
1488     }
1489     else SysFreeString(val_str);
1490     return S_OK;
1491 }
1492
1493 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1494         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1495 {
1496     FIXME("\n");
1497     return E_NOTIMPL;
1498 }
1499
1500 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1501         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1502 {
1503     FIXME("\n");
1504     return E_NOTIMPL;
1505 }
1506
1507 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1508         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1509 {
1510     FIXME("\n");
1511     return E_NOTIMPL;
1512 }
1513
1514 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1515         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1516 {
1517     FIXME("\n");
1518     return E_NOTIMPL;
1519 }
1520
1521 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1522         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1523 {
1524     FIXME("\n");
1525     return E_NOTIMPL;
1526 }
1527
1528 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1529         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1530 {
1531     FIXME("\n");
1532     return E_NOTIMPL;
1533 }
1534
1535 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1536         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1537 {
1538     StringInstance *This = (StringInstance*)dispex;
1539
1540     TRACE("\n");
1541
1542     switch(flags) {
1543     case INVOKE_FUNC:
1544         return throw_type_error(dispex->ctx, ei, IDS_NOT_FUNC, NULL);
1545     case DISPATCH_PROPERTYGET: {
1546         BSTR str = SysAllocString(This->str);
1547         if(!str)
1548             return E_OUTOFMEMORY;
1549
1550         V_VT(retv) = VT_BSTR;
1551         V_BSTR(retv) = str;
1552         break;
1553     }
1554     default:
1555         FIXME("flags %x\n", flags);
1556         return E_NOTIMPL;
1557     }
1558
1559     return S_OK;
1560 }
1561
1562 static void String_destructor(DispatchEx *dispex)
1563 {
1564     StringInstance *This = (StringInstance*)dispex;
1565
1566     heap_free(This->str);
1567     heap_free(This);
1568 }
1569
1570 static const builtin_prop_t String_props[] = {
1571     {anchorW,                String_anchor,                PROPF_METHOD},
1572     {bigW,                   String_big,                   PROPF_METHOD},
1573     {blinkW,                 String_blink,                 PROPF_METHOD},
1574     {boldW,                  String_bold,                  PROPF_METHOD},
1575     {charAtW,                String_charAt,                PROPF_METHOD},
1576     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
1577     {concatW,                String_concat,                PROPF_METHOD},
1578     {fixedW,                 String_fixed,                 PROPF_METHOD},
1579     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
1580     {fontsizeW,              String_fontsize,              PROPF_METHOD},
1581     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
1582     {indexOfW,               String_indexOf,               PROPF_METHOD},
1583     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
1584     {italicsW,               String_italics,               PROPF_METHOD},
1585     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
1586     {lengthW,                String_length,                0},
1587     {linkW,                  String_link,                  PROPF_METHOD},
1588     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
1589     {matchW,                 String_match,                 PROPF_METHOD},
1590     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1591     {replaceW,               String_replace,               PROPF_METHOD},
1592     {searchW,                String_search,                PROPF_METHOD},
1593     {sliceW,                 String_slice,                 PROPF_METHOD},
1594     {smallW,                 String_small,                 PROPF_METHOD},
1595     {splitW,                 String_split,                 PROPF_METHOD},
1596     {strikeW,                String_strike,                PROPF_METHOD},
1597     {subW,                   String_sub,                   PROPF_METHOD},
1598     {substrW,                String_substr,                PROPF_METHOD},
1599     {substringW,             String_substring,             PROPF_METHOD},
1600     {supW,                   String_sup,                   PROPF_METHOD},
1601     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1602     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1603     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1604     {toStringW,              String_toString,              PROPF_METHOD},
1605     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1606     {valueOfW,               String_valueOf,               PROPF_METHOD}
1607 };
1608
1609 static const builtin_info_t String_info = {
1610     JSCLASS_STRING,
1611     {NULL, String_value, 0},
1612     sizeof(String_props)/sizeof(*String_props),
1613     String_props,
1614     String_destructor,
1615     NULL
1616 };
1617
1618 /* ECMA-262 3rd Edition    15.5.3.2 */
1619 static HRESULT StringConstr_fromCharCode(DispatchEx *dispex, LCID lcid, WORD flags,
1620         DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1621 {
1622     DWORD i, code;
1623     BSTR ret;
1624     HRESULT hres;
1625
1626     ret = SysAllocStringLen(NULL, arg_cnt(dp));
1627     if(!ret)
1628         return E_OUTOFMEMORY;
1629
1630     for(i=0; i<arg_cnt(dp); i++) {
1631         hres = to_uint32(dispex->ctx, get_arg(dp, i), ei, &code);
1632         if(FAILED(hres)) {
1633             SysFreeString(ret);
1634             return hres;
1635         }
1636
1637         ret[i] = code;
1638     }
1639
1640     if(retv) {
1641         V_VT(retv) = VT_BSTR;
1642         V_BSTR(retv) = ret;
1643     }
1644     else SysFreeString(ret);
1645
1646     return S_OK;
1647 }
1648
1649 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1650         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1651 {
1652     HRESULT hres;
1653
1654     TRACE("\n");
1655
1656     switch(flags) {
1657     case INVOKE_FUNC: {
1658         BSTR str;
1659
1660         if(arg_cnt(dp)) {
1661             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1662             if(FAILED(hres))
1663                 return hres;
1664         }else {
1665             str = SysAllocStringLen(NULL, 0);
1666             if(!str)
1667                 return E_OUTOFMEMORY;
1668         }
1669
1670         V_VT(retv) = VT_BSTR;
1671         V_BSTR(retv) = str;
1672         break;
1673     }
1674     case DISPATCH_CONSTRUCT: {
1675         DispatchEx *ret;
1676
1677         if(arg_cnt(dp)) {
1678             BSTR str;
1679
1680             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1681             if(FAILED(hres))
1682                 return hres;
1683
1684             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1685             SysFreeString(str);
1686         }else {
1687             hres = create_string(dispex->ctx, NULL, 0, &ret);
1688         }
1689
1690         if(FAILED(hres))
1691             return hres;
1692
1693         V_VT(retv) = VT_DISPATCH;
1694         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1695         break;
1696     }
1697
1698     default:
1699         FIXME("unimplemented flags: %x\n", flags);
1700         return E_NOTIMPL;
1701     }
1702
1703     return S_OK;
1704 }
1705
1706 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1707 {
1708     StringInstance *string;
1709     HRESULT hres;
1710
1711     string = heap_alloc_zero(sizeof(StringInstance));
1712     if(!string)
1713         return E_OUTOFMEMORY;
1714
1715     if(use_constr)
1716         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1717     else
1718         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1719     if(FAILED(hres)) {
1720         heap_free(string);
1721         return hres;
1722     }
1723
1724     *ret = string;
1725     return S_OK;
1726 }
1727
1728 static const builtin_prop_t StringConstr_props[] = {
1729     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1730 };
1731
1732 static const builtin_info_t StringConstr_info = {
1733     JSCLASS_FUNCTION,
1734     {NULL, Function_value, 0},
1735     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1736     StringConstr_props,
1737     NULL,
1738     NULL
1739 };
1740
1741 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1742 {
1743     StringInstance *string;
1744     HRESULT hres;
1745
1746     hres = string_alloc(ctx, FALSE, &string);
1747     if(FAILED(hres))
1748         return hres;
1749
1750     hres = create_builtin_function(ctx, StringConstr_value, &StringConstr_info, PROPF_CONSTR, &string->dispex, ret);
1751
1752     jsdisp_release(&string->dispex);
1753     return hres;
1754 }
1755
1756 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1757 {
1758     StringInstance *string;
1759     HRESULT hres;
1760
1761     hres = string_alloc(ctx, TRUE, &string);
1762     if(FAILED(hres))
1763         return hres;
1764
1765     if(len == -1)
1766         len = strlenW(str);
1767
1768     string->length = len;
1769     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1770     if(!string->str) {
1771         jsdisp_release(&string->dispex);
1772         return E_OUTOFMEMORY;
1773     }
1774
1775     memcpy(string->str, str, len*sizeof(WCHAR));
1776     string->str[len] = 0;
1777
1778     *ret = &string->dispex;
1779     return S_OK;
1780
1781 }