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