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