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