comctl32: Remove superfluous casts that crept in as well as some older ones.
[wine] / dlls / jscript / string.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "jscript.h"
20
21 #include "wine/debug.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
24
25 typedef struct {
26     DispatchEx dispex;
27
28     WCHAR *str;
29     DWORD length;
30 } StringInstance;
31
32 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
33 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
34 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
35 static const WCHAR anchorW[] = {'a','n','c','h','o','r',0};
36 static const WCHAR bigW[] = {'b','i','g',0};
37 static const WCHAR blinkW[] = {'b','l','i','n','k',0};
38 static const WCHAR boldW[] = {'b','o','l','d',0};
39 static const WCHAR charAtW[] = {'c','h','a','r','A','t',0};
40 static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0};
41 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
42 static const WCHAR fixedW[] = {'f','i','x','e','d',0};
43 static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0};
44 static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0};
45 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0};
46 static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0};
47 static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
48 static const WCHAR linkW[] = {'l','i','n','k',0};
49 static const WCHAR matchW[] = {'m','a','t','c','h',0};
50 static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0};
51 static const WCHAR searchW[] = {'s','e','a','r','c','h',0};
52 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
53 static const WCHAR smallW[] = {'s','m','a','l','l',0};
54 static const WCHAR splitW[] = {'s','p','l','i','t',0};
55 static const WCHAR strikeW[] = {'s','t','r','i','k','e',0};
56 static const WCHAR subW[] = {'s','u','b',0};
57 static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0};
58 static const WCHAR substrW[] = {'s','u','b','s','t','r',0};
59 static const WCHAR supW[] = {'s','u','p',0};
60 static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
61 static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
62 static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
63 static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
64 static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
65 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
66 static const WCHAR propertyIsEnumerableW[] =
67     {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
68 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
69
70 static HRESULT String_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
71         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
72 {
73     TRACE("%p\n", dispex);
74
75     switch(flags) {
76     case DISPATCH_PROPERTYGET: {
77         StringInstance *jsthis = (StringInstance*)dispex;
78
79         V_VT(retv) = VT_I4;
80         V_I4(retv) = jsthis->length;
81         break;
82     }
83     default:
84         FIXME("unimplemented flags %x\n", flags);
85         return E_NOTIMPL;
86     }
87
88     return S_OK;
89 }
90
91 /* ECMA-262 3rd Edition    15.5.4.2 */
92 static HRESULT String_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
93         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
94 {
95     StringInstance *string;
96
97     TRACE("\n");
98
99     if(!is_class(dispex, JSCLASS_STRING)) {
100         WARN("this is not a string object\n");
101         return E_FAIL;
102     }
103
104     string = (StringInstance*)dispex;
105
106     if(retv) {
107         BSTR str = SysAllocString(string->str);
108         if(!str)
109             return E_OUTOFMEMORY;
110
111         V_VT(retv) = VT_BSTR;
112         V_BSTR(retv) = str;
113     }
114     return S_OK;
115 }
116
117 /* ECMA-262 3rd Edition    15.5.4.2 */
118 static HRESULT String_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
119         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
120 {
121     TRACE("\n");
122
123     return String_toString(dispex, lcid, flags, dp, retv, ei, sp);
124 }
125
126 static HRESULT String_anchor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
127         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
128 {
129     FIXME("\n");
130     return E_NOTIMPL;
131 }
132
133 static HRESULT String_big(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
134         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
135 {
136     FIXME("\n");
137     return E_NOTIMPL;
138 }
139
140 static HRESULT String_blink(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
141         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
142 {
143     FIXME("\n");
144     return E_NOTIMPL;
145 }
146
147 static HRESULT String_bold(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
148         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
149 {
150     FIXME("\n");
151     return E_NOTIMPL;
152 }
153
154 /* ECMA-262 3rd Edition    15.5.4.5 */
155 static HRESULT String_charAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
156         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
157 {
158     StringInstance *strobj;
159     BSTR str;
160     INT pos = 0;
161     HRESULT hres;
162
163     TRACE("\n");
164
165     if(dispex->builtin_info->class != JSCLASS_STRING) {
166         FIXME("not string this not supported\n");
167         return E_NOTIMPL;
168     }
169
170     strobj = (StringInstance*)dispex;
171
172     if(arg_cnt(dp)) {
173         VARIANT num;
174
175         hres = to_integer(dispex->ctx, get_arg(dp, 0), ei, &num);
176         if(FAILED(hres))
177             return hres;
178
179         if(V_VT(&num) == VT_I4) {
180             pos = V_I4(&num);
181         }else {
182             WARN("pos = %lf\n", V_R8(&num));
183             pos = -1;
184         }
185     }
186
187     if(!retv)
188         return S_OK;
189
190     if(0 <= pos && pos < strobj->length)
191         str = SysAllocStringLen(strobj->str+pos, 1);
192     else
193         str = SysAllocStringLen(NULL, 0);
194     if(!str)
195         return E_OUTOFMEMORY;
196
197     V_VT(retv) = VT_BSTR;
198     V_BSTR(retv) = str;
199     return S_OK;
200 }
201
202 /* ECMA-262 3rd Edition    15.5.4.5 */
203 static HRESULT String_charCodeAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
204         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
205 {
206     const WCHAR *str;
207     DWORD length, idx = 0;
208     HRESULT hres;
209
210     TRACE("\n");
211
212     if(dispex->builtin_info->class == JSCLASS_STRING) {
213         StringInstance *string = (StringInstance*)dispex;
214
215         str = string->str;
216         length = string->length;
217     }else {
218         FIXME("not string this not supported\n");
219         return E_NOTIMPL;
220     }
221
222     if(arg_cnt(dp) > 0) {
223         VARIANT v;
224
225         hres = to_integer(dispex->ctx, get_arg(dp, 0), ei, &v);
226         if(FAILED(hres))
227             return hres;
228
229         if(V_VT(&v) != VT_I4 || V_I4(&v) < 0 || V_I4(&v) >= length) {
230             FIXME("NAN\n");
231             return E_FAIL;
232         }
233
234         idx = V_I4(&v);
235     }
236
237     if(retv) {
238         V_VT(retv) = VT_I4;
239         V_I4(retv) = str[idx];
240     }
241     return S_OK;
242 }
243
244 /* ECMA-262 3rd Edition    15.5.4.6 */
245 static HRESULT String_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
246         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
247 {
248     BSTR *strs = NULL, ret = NULL;
249     DWORD len = 0, i, l, str_cnt;
250     VARIANT var;
251     WCHAR *ptr;
252     HRESULT hres;
253
254     TRACE("\n");
255
256     str_cnt = arg_cnt(dp)+1;
257     strs = heap_alloc_zero(str_cnt * sizeof(BSTR));
258     if(!strs)
259         return E_OUTOFMEMORY;
260
261     V_VT(&var) = VT_DISPATCH;
262     V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(dispex);
263
264     hres = to_string(dispex->ctx, &var, ei, strs);
265     if(SUCCEEDED(hres)) {
266         for(i=0; i < arg_cnt(dp); i++) {
267             hres = to_string(dispex->ctx, get_arg(dp, i), ei, strs+i+1);
268             if(FAILED(hres))
269                 break;
270         }
271     }
272
273     if(SUCCEEDED(hres)) {
274         for(i=0; i < str_cnt; i++)
275             len += SysStringLen(strs[i]);
276
277         ptr = ret = SysAllocStringLen(NULL, len);
278
279         for(i=0; i < str_cnt; i++) {
280             l = SysStringLen(strs[i]);
281             memcpy(ptr, strs[i], l*sizeof(WCHAR));
282             ptr += l;
283         }
284     }
285
286     for(i=0; i < str_cnt; i++)
287         SysFreeString(strs[i]);
288     heap_free(strs);
289
290     if(FAILED(hres))
291         return hres;
292
293     if(retv) {
294         V_VT(retv) = VT_BSTR;
295         V_BSTR(retv) = ret;
296     }else {
297         SysFreeString(ret);
298     }
299     return S_OK;
300 }
301
302 static HRESULT String_fixed(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
303         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
304 {
305     FIXME("\n");
306     return E_NOTIMPL;
307 }
308
309 static HRESULT String_fontcolor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
310         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
311 {
312     FIXME("\n");
313     return E_NOTIMPL;
314 }
315
316 static HRESULT String_fontsize(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
317         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
318 {
319     FIXME("\n");
320     return E_NOTIMPL;
321 }
322
323 static HRESULT String_indexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
324         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
325 {
326     DWORD length, pos = 0;
327     const WCHAR *str;
328     BSTR search_str;
329     INT ret = -1;
330     HRESULT hres;
331
332     TRACE("\n");
333
334     if(is_class(dispex, JSCLASS_STRING)) {
335         StringInstance *string = (StringInstance*)dispex;
336
337         str = string->str;
338         length = string->length;
339     }else {
340         FIXME("not String this\n");
341         return E_NOTIMPL;
342     }
343
344     if(!arg_cnt(dp)) {
345         if(retv) {
346             V_VT(retv) = VT_I4;
347             V_I4(retv) = -1;
348         }
349         return S_OK;
350     }
351
352     hres = to_string(dispex->ctx, get_arg(dp,0), ei, &search_str);
353     if(FAILED(hres))
354         return hres;
355
356     if(arg_cnt(dp) >= 2) {
357         VARIANT ival;
358
359         hres = to_integer(dispex->ctx, get_arg(dp,1), ei, &ival);
360         if(SUCCEEDED(hres)) {
361             if(V_VT(&ival) == VT_I4)
362                 pos = V_VT(&ival) > 0 ? V_I4(&ival) : 0;
363             else
364                 pos = V_R8(&ival) > 0.0 ? length : 0;
365             if(pos > length)
366                 pos = length;
367         }
368     }
369
370     if(SUCCEEDED(hres)) {
371         const WCHAR *ptr;
372
373         ptr = strstrW(str+pos, search_str);
374         if(ptr)
375             ret = ptr - str;
376         else
377             ret = -1;
378     }
379
380     SysFreeString(search_str);
381     if(FAILED(hres))
382         return hres;
383
384     if(retv) {
385         V_VT(retv) = VT_I4;
386         V_I4(retv) = ret;
387     }
388     return S_OK;
389 }
390
391 static HRESULT String_italics(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
392         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
393 {
394     FIXME("\n");
395     return E_NOTIMPL;
396 }
397
398 static HRESULT String_lastIndexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
399         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
400 {
401     FIXME("\n");
402     return E_NOTIMPL;
403 }
404
405 static HRESULT String_link(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
406         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
407 {
408     FIXME("\n");
409     return E_NOTIMPL;
410 }
411
412 /* ECMA-262 3rd Edition    15.5.4.10 */
413 static HRESULT String_match(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
414         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
415 {
416     StringInstance *This = (StringInstance*)dispex;
417     match_result_t *match_result;
418     DispatchEx *regexp;
419     DispatchEx *array;
420     VARIANT var, *arg_var;
421     DWORD match_cnt, i;
422     HRESULT hres = S_OK;
423
424     TRACE("\n");
425
426     if(arg_cnt(dp) != 1) {
427         FIXME("unsupported args\n");
428         return E_NOTIMPL;
429     }
430
431     arg_var = get_arg(dp, 0);
432     switch(V_VT(arg_var)) {
433     case VT_DISPATCH:
434         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
435         if(regexp) {
436             if(regexp->builtin_info->class == JSCLASS_REGEXP)
437                 break;
438             jsdisp_release(regexp);
439         }
440     default: {
441         BSTR match_str;
442
443         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
444         if(FAILED(hres))
445             return hres;
446
447         hres = create_regexp_str(dispex->ctx, match_str, SysStringLen(match_str), NULL, 0, &regexp);
448         SysFreeString(match_str);
449         if(FAILED(hres))
450             return hres;
451     }
452     }
453
454     hres = regexp_match(regexp, This->str, This->length, FALSE, &match_result, &match_cnt);
455     jsdisp_release(regexp);
456     if(FAILED(hres))
457         return hres;
458
459     if(!match_cnt) {
460         TRACE("no match\n");
461
462         if(retv)
463             V_VT(retv) = VT_NULL;
464         return S_OK;
465     }
466
467     hres = create_array(dispex->ctx, match_cnt, &array);
468     if(FAILED(hres))
469         return hres;
470
471     V_VT(&var) = VT_BSTR;
472
473     for(i=0; i < match_cnt; i++) {
474         V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len);
475         if(!V_BSTR(&var)) {
476             hres = E_OUTOFMEMORY;
477             break;
478         }
479
480         hres = jsdisp_propput_idx(array, i, lcid, &var, ei, NULL/*FIXME*/);
481         SysFreeString(V_BSTR(&var));
482         if(FAILED(hres))
483             break;
484     }
485
486     if(SUCCEEDED(hres) && retv) {
487         V_VT(retv) = VT_DISPATCH;
488         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
489     }else {
490         jsdisp_release(array);
491     }
492     return hres;
493 }
494
495 typedef struct {
496     WCHAR *buf;
497     DWORD size;
498     DWORD len;
499 } strbuf_t;
500
501 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
502 {
503     if(!len)
504         return S_OK;
505
506     if(len + buf->len > buf->size) {
507         WCHAR *new_buf;
508         DWORD new_size;
509
510         new_size = buf->size ? buf->size<<1 : 16;
511         if(new_size < buf->len+len)
512             new_size = buf->len+len;
513         if(buf->buf)
514             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
515         else
516             new_buf = heap_alloc(new_size*sizeof(WCHAR));
517         if(!new_buf)
518             return E_OUTOFMEMORY;
519
520         buf->buf = new_buf;
521         buf->size = new_size;
522     }
523
524     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
525     buf->len += len;
526     return S_OK;
527 }
528
529 static HRESULT rep_call(DispatchEx *func, const WCHAR *str, match_result_t *match, match_result_t *parens,
530         DWORD parens_cnt, LCID lcid, BSTR *ret, jsexcept_t *ei, IServiceProvider *caller)
531 {
532     DISPPARAMS dp = {NULL, NULL, 0, 0};
533     VARIANTARG *args, *arg;
534     VARIANT var;
535     DWORD i;
536     HRESULT hres = S_OK;
537
538     dp.cArgs = parens_cnt+3;
539     dp.rgvarg = args = heap_alloc_zero(sizeof(VARIANT)*dp.cArgs);
540     if(!args)
541         return E_OUTOFMEMORY;
542
543     arg = get_arg(&dp,0);
544     V_VT(arg) = VT_BSTR;
545     V_BSTR(arg) = SysAllocStringLen(match->str, match->len);
546     if(!V_BSTR(arg))
547         hres = E_OUTOFMEMORY;
548
549     if(SUCCEEDED(hres)) {
550         for(i=0; i < parens_cnt; i++) {
551             arg = get_arg(&dp,i+1);
552             V_VT(arg) = VT_BSTR;
553             V_BSTR(arg) = SysAllocStringLen(parens[i].str, parens[i].len);
554             if(!V_BSTR(arg)) {
555                hres = E_OUTOFMEMORY;
556                break;
557             }
558         }
559     }
560
561     if(SUCCEEDED(hres)) {
562         arg = get_arg(&dp,parens_cnt+1);
563         V_VT(arg) = VT_I4;
564         V_I4(arg) = match->str - str;
565
566         arg = get_arg(&dp,parens_cnt+2);
567         V_VT(arg) = VT_BSTR;
568         V_BSTR(arg) = SysAllocString(str);
569         if(!V_BSTR(arg))
570             hres = E_OUTOFMEMORY;
571     }
572
573     if(SUCCEEDED(hres))
574         hres = jsdisp_call_value(func, lcid, DISPATCH_METHOD, &dp, &var, ei, caller);
575
576     for(i=0; i < parens_cnt+1; i++) {
577         if(i != parens_cnt+1)
578             SysFreeString(V_BSTR(get_arg(&dp,i)));
579     }
580     heap_free(args);
581
582     if(FAILED(hres))
583         return hres;
584
585     hres = to_string(func->ctx, &var, ei, ret);
586     VariantClear(&var);
587     return hres;
588 }
589
590 /* ECMA-262 3rd Edition    15.5.4.11 */
591 static HRESULT String_replace(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
592         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
593 {
594     DWORD parens_cnt = 0, parens_size=0, rep_len=0, length;
595     BSTR rep_str = NULL, match_str = NULL, ret_str;
596     DispatchEx *rep_func = NULL, *regexp = NULL;
597     match_result_t *parens = NULL, match;
598     const WCHAR *str;
599     strbuf_t ret = {NULL,0,0};
600     BOOL gcheck = FALSE;
601     VARIANT *arg_var;
602     HRESULT hres = S_OK;
603
604     TRACE("\n");
605
606     if(is_class(dispex, JSCLASS_STRING)) {
607         StringInstance *string = (StringInstance*)dispex;
608         str = string->str;
609         length = string->length;
610     }else {
611         FIXME("not String this\n");
612         return E_NOTIMPL;
613     }
614
615     if(!arg_cnt(dp)) {
616         if(retv) {
617             ret_str = SysAllocString(str);
618             if(!ret_str)
619                 return E_OUTOFMEMORY;
620
621             V_VT(retv) = VT_BSTR;
622             V_BSTR(retv) = ret_str;
623         }
624         return S_OK;
625     }
626
627     arg_var = get_arg(dp, 0);
628     switch(V_VT(arg_var)) {
629     case VT_DISPATCH:
630         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
631         if(regexp) {
632             if(is_class(regexp, JSCLASS_REGEXP)) {
633                 break;
634             }else {
635                 jsdisp_release(regexp);
636                 regexp = NULL;
637             }
638         }
639
640     default:
641         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
642         if(FAILED(hres))
643             return hres;
644     }
645
646     if(arg_cnt(dp) >= 2) {
647         arg_var = get_arg(dp,1);
648         switch(V_VT(arg_var)) {
649         case VT_DISPATCH:
650             rep_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
651             if(rep_func) {
652                 if(is_class(rep_func, JSCLASS_FUNCTION)) {
653                     break;
654                 }else {
655                     jsdisp_release(rep_func);
656                     rep_func = NULL;
657                 }
658             }
659
660         default:
661             hres = to_string(dispex->ctx, arg_var, ei, &rep_str);
662             if(FAILED(hres))
663                 break;
664
665             if(strchrW(rep_str, '$')) {
666                 FIXME("unsupported $ in replace string\n");
667                 hres = E_NOTIMPL;
668             }
669
670             rep_len = SysStringLen(rep_str);
671         }
672     }
673
674     if(SUCCEEDED(hres)) {
675         const WCHAR *cp, *ecp;
676
677         cp = ecp = str;
678
679         while(1) {
680             if(regexp) {
681                 hres = regexp_match_next(regexp, gcheck, str, length, &cp, rep_func ? &parens : NULL,
682                                          &parens_size, &parens_cnt, &match);
683                 gcheck = TRUE;
684
685                 if(hres == S_FALSE) {
686                     hres = S_OK;
687                     break;
688                 }
689                 if(FAILED(hres))
690                     break;
691             }else {
692                 match.str = strstrW(cp, match_str);
693                 if(!match.str)
694                     break;
695                 match.len = SysStringLen(match_str);
696                 cp = match.str+match.len;
697             }
698
699             hres = strbuf_append(&ret, ecp, match.str-ecp);
700             ecp = match.str+match.len;
701             if(FAILED(hres))
702                 break;
703
704             if(rep_func) {
705                 BSTR cstr;
706
707                 hres = rep_call(rep_func, str, &match, parens, parens_cnt, lcid, &cstr, ei, caller);
708                 if(FAILED(hres))
709                     break;
710
711                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
712                 SysFreeString(cstr);
713                 if(FAILED(hres))
714                     break;
715             }else if(rep_str) {
716                 hres = strbuf_append(&ret, rep_str, rep_len);
717                 if(FAILED(hres))
718                     break;
719             }else {
720                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
721
722                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
723                 if(FAILED(hres))
724                     break;
725             }
726         }
727
728         if(SUCCEEDED(hres))
729             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
730     }
731
732     if(rep_func)
733         jsdisp_release(rep_func);
734     if(regexp)
735         jsdisp_release(regexp);
736     SysFreeString(rep_str);
737     SysFreeString(match_str);
738     heap_free(parens);
739
740     if(SUCCEEDED(hres) && retv) {
741         ret_str = SysAllocStringLen(ret.buf, ret.len);
742         if(!ret_str)
743             return E_OUTOFMEMORY;
744
745         V_VT(retv) = VT_BSTR;
746         V_BSTR(retv) = ret_str;
747         TRACE("= %s\n", debugstr_w(ret_str));
748     }
749
750     heap_free(ret.buf);
751     return hres;
752 }
753
754 static HRESULT String_search(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
755         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
756 {
757     FIXME("\n");
758     return E_NOTIMPL;
759 }
760
761 /* ECMA-262 3rd Edition    15.5.4.13 */
762 static HRESULT String_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
763         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
764 {
765     const WCHAR *str;
766     DWORD length;
767     INT start=0, end;
768     VARIANT v;
769     HRESULT hres;
770
771     TRACE("\n");
772
773     if(is_class(dispex, JSCLASS_STRING)) {
774         StringInstance *string = (StringInstance*)dispex;
775
776         str = string->str;
777         length = string->length;
778     }else {
779         FIXME("this is not a string class\n");
780         return E_NOTIMPL;
781     }
782
783     if(arg_cnt(dp)) {
784         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
785         if(FAILED(hres))
786             return hres;
787
788         if(V_VT(&v) == VT_I4) {
789             start = V_I4(&v);
790             if(start < 0) {
791                 start = length + start;
792                 if(start < 0)
793                     start = 0;
794             }else if(start > length) {
795                 start = length;
796             }
797         }else {
798             start = V_R8(&v) < 0.0 ? 0 : length;
799         }
800     }else {
801         start = 0;
802     }
803
804     if(arg_cnt(dp) >= 2) {
805         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
806         if(FAILED(hres))
807             return hres;
808
809         if(V_VT(&v) == VT_I4) {
810             end = V_I4(&v);
811             if(end < 0) {
812                 end = length + end;
813                 if(end < 0)
814                     end = 0;
815             }else if(end > length) {
816                 end = length;
817             }
818         }else {
819             end = V_R8(&v) < 0.0 ? 0 : length;
820         }
821     }else {
822         end = length;
823     }
824
825     if(end < start)
826         end = start;
827
828     if(retv) {
829         BSTR retstr = SysAllocStringLen(str+start, end-start);
830         if(!str)
831             return E_OUTOFMEMORY;
832
833         V_VT(retv) = VT_BSTR;
834         V_BSTR(retv) = retstr;
835     }
836     return S_OK;
837 }
838
839 static HRESULT String_small(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
840         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
841 {
842     FIXME("\n");
843     return E_NOTIMPL;
844 }
845
846 static HRESULT String_split(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
847         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
848 {
849     match_result_t *match_result = NULL;
850     DWORD match_cnt, i, match_len = 0;
851     StringInstance *string;
852     const WCHAR *ptr, *ptr2;
853     VARIANT *arg, var;
854     DispatchEx *array;
855     BSTR match_str = NULL;
856     HRESULT hres;
857
858     TRACE("\n");
859
860     if(!is_class(dispex, JSCLASS_STRING)) {
861         FIXME("not String this\n");
862         return E_NOTIMPL;
863     }
864
865     string = (StringInstance*)dispex;
866
867     if(arg_cnt(dp) != 1) {
868         FIXME("unsupported args\n");
869         return E_NOTIMPL;
870     }
871
872     arg = get_arg(dp, 0);
873     switch(V_VT(arg)) {
874     case VT_DISPATCH: {
875         DispatchEx *regexp;
876
877         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
878         if(regexp) {
879             if(is_class(regexp, JSCLASS_REGEXP)) {
880                 hres = regexp_match(regexp, string->str, string->length, TRUE, &match_result, &match_cnt);
881                 jsdisp_release(regexp);
882                 if(FAILED(hres))
883                     return hres;
884                 break;
885             }
886             jsdisp_release(regexp);
887         }
888     }
889     default:
890         hres = to_string(dispex->ctx, arg, ei, &match_str);
891         if(FAILED(hres))
892             return hres;
893
894         match_len = SysStringLen(match_str);
895         if(!match_len) {
896             SysFreeString(match_str);
897             match_str = NULL;
898         }
899     }
900
901     hres = create_array(dispex->ctx, 0, &array);
902
903     if(SUCCEEDED(hres)) {
904         ptr = string->str;
905         for(i=0;; i++) {
906             if(match_result) {
907                 if(i == match_cnt)
908                     break;
909                 ptr2 = match_result[i].str;
910             }else if(match_str) {
911                 ptr2 = strstrW(ptr, match_str);
912                 if(!ptr2)
913                     break;
914             }else {
915                 if(!*ptr)
916                     break;
917                 ptr2 = ptr+1;
918             }
919
920             V_VT(&var) = VT_BSTR;
921             V_BSTR(&var) = SysAllocStringLen(ptr, ptr2-ptr);
922             if(!V_BSTR(&var)) {
923                 hres = E_OUTOFMEMORY;
924                 break;
925             }
926
927             hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
928             SysFreeString(V_BSTR(&var));
929             if(FAILED(hres))
930                 break;
931
932             if(match_result)
933                 ptr = match_result[i].str + match_result[i].len;
934             else if(match_str)
935                 ptr = ptr2 + match_len;
936             else
937                 ptr++;
938         }
939     }
940
941     if(SUCCEEDED(hres) && (match_str || match_result)) {
942         DWORD len = (string->str+string->length) - ptr;
943
944         if(len || match_str) {
945             V_VT(&var) = VT_BSTR;
946             V_BSTR(&var) = SysAllocStringLen(ptr, len);
947
948             if(V_BSTR(&var)) {
949                 hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
950                 SysFreeString(V_BSTR(&var));
951             }else {
952                 hres = E_OUTOFMEMORY;
953             }
954         }
955     }
956
957     SysFreeString(match_str);
958     heap_free(match_result);
959
960     if(SUCCEEDED(hres) && retv) {
961         V_VT(retv) = VT_DISPATCH;
962         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
963     }else {
964         jsdisp_release(array);
965     }
966
967     return hres;
968 }
969
970 static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
971         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
972 {
973     FIXME("\n");
974     return E_NOTIMPL;
975 }
976
977 static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
978         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
979 {
980     FIXME("\n");
981     return E_NOTIMPL;
982 }
983
984 /* ECMA-262 3rd Edition    15.5.4.15 */
985 static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
986         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
987 {
988     const WCHAR *str;
989     INT start=0, end;
990     DWORD length;
991     VARIANT v;
992     HRESULT hres;
993
994     TRACE("\n");
995
996     if(is_class(dispex, JSCLASS_STRING)) {
997         StringInstance *string = (StringInstance*)dispex;
998
999         length = string->length;
1000         str = string->str;
1001     }else {
1002         FIXME("not string this not supported\n");
1003         return E_NOTIMPL;
1004     }
1005
1006     if(arg_cnt(dp) >= 1) {
1007         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
1008         if(FAILED(hres))
1009             return hres;
1010
1011         if(V_VT(&v) == VT_I4) {
1012             start = V_I4(&v);
1013             if(start < 0)
1014                 start = 0;
1015             else if(start >= length)
1016                 start = length;
1017         }else {
1018             start = V_R8(&v) < 0.0 ? 0 : length;
1019         }
1020     }
1021
1022     if(arg_cnt(dp) >= 2) {
1023         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
1024         if(FAILED(hres))
1025             return hres;
1026
1027         if(V_VT(&v) == VT_I4) {
1028             end = V_I4(&v);
1029             if(end < 0)
1030                 end = 0;
1031             else if(end > length)
1032                 end = length;
1033         }else {
1034             end = V_R8(&v) < 0.0 ? 0 : length;
1035         }
1036     }else {
1037         end = length;
1038     }
1039
1040     if(start > end) {
1041         INT tmp = start;
1042         start = end;
1043         end = tmp;
1044     }
1045
1046     if(retv) {
1047         V_VT(retv) = VT_BSTR;
1048         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
1049         if(!V_BSTR(retv))
1050             return E_OUTOFMEMORY;
1051     }
1052     return S_OK;
1053 }
1054
1055 static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1056         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1057 {
1058     FIXME("\n");
1059     return E_NOTIMPL;
1060 }
1061
1062 static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1063         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1064 {
1065     FIXME("\n");
1066     return E_NOTIMPL;
1067 }
1068
1069 static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1070         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1071 {
1072     FIXME("\n");
1073     return E_NOTIMPL;
1074 }
1075
1076 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1077         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1078 {
1079     FIXME("\n");
1080     return E_NOTIMPL;
1081 }
1082
1083 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1084         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1085 {
1086     FIXME("\n");
1087     return E_NOTIMPL;
1088 }
1089
1090 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1091         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1092 {
1093     FIXME("\n");
1094     return E_NOTIMPL;
1095 }
1096
1097 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1098         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1099 {
1100     FIXME("\n");
1101     return E_NOTIMPL;
1102 }
1103
1104 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1105         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1106 {
1107     FIXME("\n");
1108     return E_NOTIMPL;
1109 }
1110
1111 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1112         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1113 {
1114     FIXME("\n");
1115     return E_NOTIMPL;
1116 }
1117
1118 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1119         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1120 {
1121     FIXME("\n");
1122     return E_NOTIMPL;
1123 }
1124
1125 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1126         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1127 {
1128     StringInstance *This = (StringInstance*)dispex;
1129
1130     TRACE("\n");
1131
1132     switch(flags) {
1133     case DISPATCH_PROPERTYGET: {
1134         BSTR str = SysAllocString(This->str);
1135         if(!str)
1136             return E_OUTOFMEMORY;
1137
1138         V_VT(retv) = VT_BSTR;
1139         V_BSTR(retv) = str;
1140         break;
1141     }
1142     default:
1143         FIXME("flags %x\n", flags);
1144         return E_NOTIMPL;
1145     }
1146
1147     return S_OK;
1148 }
1149
1150 static void String_destructor(DispatchEx *dispex)
1151 {
1152     StringInstance *This = (StringInstance*)dispex;
1153
1154     heap_free(This->str);
1155     heap_free(This);
1156 }
1157
1158 static const builtin_prop_t String_props[] = {
1159     {anchorW,                String_anchor,                PROPF_METHOD},
1160     {bigW,                   String_big,                   PROPF_METHOD},
1161     {blinkW,                 String_blink,                 PROPF_METHOD},
1162     {boldW,                  String_bold,                  PROPF_METHOD},
1163     {charAtW,                String_charAt,                PROPF_METHOD},
1164     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
1165     {concatW,                String_concat,                PROPF_METHOD},
1166     {fixedW,                 String_fixed,                 PROPF_METHOD},
1167     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
1168     {fontsizeW,              String_fontsize,              PROPF_METHOD},
1169     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
1170     {indexOfW,               String_indexOf,               PROPF_METHOD},
1171     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
1172     {italicsW,               String_italics,               PROPF_METHOD},
1173     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
1174     {lengthW,                String_length,                0},
1175     {linkW,                  String_link,                  PROPF_METHOD},
1176     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
1177     {matchW,                 String_match,                 PROPF_METHOD},
1178     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1179     {replaceW,               String_replace,               PROPF_METHOD},
1180     {searchW,                String_search,                PROPF_METHOD},
1181     {sliceW,                 String_slice,                 PROPF_METHOD},
1182     {smallW,                 String_small,                 PROPF_METHOD},
1183     {splitW,                 String_split,                 PROPF_METHOD},
1184     {strikeW,                String_strike,                PROPF_METHOD},
1185     {substringW,             String_substring,             PROPF_METHOD},
1186     {substrW,                String_substr,                PROPF_METHOD},
1187     {subW,                   String_sub,                   PROPF_METHOD},
1188     {supW,                   String_sup,                   PROPF_METHOD},
1189     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1190     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1191     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1192     {toStringW,              String_toString,              PROPF_METHOD},
1193     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1194     {valueOfW,               String_valueOf,               PROPF_METHOD}
1195 };
1196
1197 static const builtin_info_t String_info = {
1198     JSCLASS_STRING,
1199     {NULL, String_value, 0},
1200     sizeof(String_props)/sizeof(*String_props),
1201     String_props,
1202     String_destructor,
1203     NULL
1204 };
1205
1206 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1207         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1208 {
1209     HRESULT hres;
1210
1211     TRACE("\n");
1212
1213     switch(flags) {
1214     case INVOKE_FUNC: {
1215         BSTR str;
1216
1217         if(arg_cnt(dp)) {
1218             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1219             if(FAILED(hres))
1220                 return hres;
1221         }else {
1222             str = SysAllocStringLen(NULL, 0);
1223             if(!str)
1224                 return E_OUTOFMEMORY;
1225         }
1226
1227         V_VT(retv) = VT_BSTR;
1228         V_BSTR(retv) = str;
1229         break;
1230     }
1231     case DISPATCH_CONSTRUCT: {
1232         DispatchEx *ret;
1233
1234         if(arg_cnt(dp)) {
1235             BSTR str;
1236
1237             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1238             if(FAILED(hres))
1239                 return hres;
1240
1241             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1242             SysFreeString(str);
1243         }else {
1244             hres = create_string(dispex->ctx, NULL, 0, &ret);
1245         }
1246
1247         if(FAILED(hres))
1248             return hres;
1249
1250         V_VT(retv) = VT_DISPATCH;
1251         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1252         break;
1253     }
1254
1255     default:
1256         FIXME("unimplemented flags: %x\n", flags);
1257         return E_NOTIMPL;
1258     }
1259
1260     return S_OK;
1261 }
1262
1263 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1264 {
1265     StringInstance *string;
1266     HRESULT hres;
1267
1268     string = heap_alloc_zero(sizeof(StringInstance));
1269     if(!string)
1270         return E_OUTOFMEMORY;
1271
1272     if(use_constr)
1273         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1274     else
1275         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1276     if(FAILED(hres)) {
1277         heap_free(string);
1278         return hres;
1279     }
1280
1281     *ret = string;
1282     return S_OK;
1283 }
1284
1285 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1286 {
1287     StringInstance *string;
1288     HRESULT hres;
1289
1290     hres = string_alloc(ctx, FALSE, &string);
1291     if(FAILED(hres))
1292         return hres;
1293
1294     hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret);
1295
1296     jsdisp_release(&string->dispex);
1297     return hres;
1298 }
1299
1300 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1301 {
1302     StringInstance *string;
1303     HRESULT hres;
1304
1305     hres = string_alloc(ctx, TRUE, &string);
1306     if(FAILED(hres))
1307         return hres;
1308
1309     if(len == -1)
1310         len = strlenW(str);
1311
1312     string->length = len;
1313     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1314     if(!string->str) {
1315         jsdisp_release(&string->dispex);
1316         return E_OUTOFMEMORY;
1317     }
1318
1319     memcpy(string->str, str, len*sizeof(WCHAR));
1320     string->str[len] = 0;
1321
1322     *ret = &string->dispex;
1323     return S_OK;
1324
1325 }