jscript: Implement the String.toUpperCase() method.
[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     StringInstance *string;
1073     const WCHAR* str;
1074     DWORD length;
1075     BSTR bstr;
1076
1077     TRACE("\n");
1078
1079     if(is_class(dispex, JSCLASS_STRING)) {
1080         string = (StringInstance*)dispex;
1081
1082         length = string->length;
1083         str = string->str;
1084     }else {
1085         FIXME("not string this not supported\n");
1086         return E_NOTIMPL;
1087     }
1088
1089     if(retv) {
1090         bstr = SysAllocStringLen(str, length);
1091         if (!bstr)
1092             return E_OUTOFMEMORY;
1093
1094         strlwrW(bstr);
1095
1096         V_VT(retv) = VT_BSTR;
1097         V_BSTR(retv) = bstr;
1098     }
1099     return S_OK;
1100 }
1101
1102 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1103         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1104 {
1105     StringInstance *string;
1106     const WCHAR* str;
1107     DWORD length;
1108     BSTR bstr;
1109
1110     TRACE("\n");
1111
1112     if(is_class(dispex, JSCLASS_STRING)) {
1113         string = (StringInstance*)dispex;
1114
1115         length = string->length;
1116         str = string->str;
1117     }else {
1118         FIXME("not string this not supported\n");
1119         return E_NOTIMPL;
1120     }
1121
1122     if(retv) {
1123         bstr = SysAllocStringLen(str, length);
1124         if (!bstr)
1125             return E_OUTOFMEMORY;
1126
1127         struprW(bstr);
1128
1129         V_VT(retv) = VT_BSTR;
1130         V_BSTR(retv) = bstr;
1131     }
1132     return S_OK;
1133 }
1134
1135 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1136         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1137 {
1138     FIXME("\n");
1139     return E_NOTIMPL;
1140 }
1141
1142 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1143         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1144 {
1145     FIXME("\n");
1146     return E_NOTIMPL;
1147 }
1148
1149 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1150         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1151 {
1152     FIXME("\n");
1153     return E_NOTIMPL;
1154 }
1155
1156 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1157         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1158 {
1159     FIXME("\n");
1160     return E_NOTIMPL;
1161 }
1162
1163 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1164         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1165 {
1166     FIXME("\n");
1167     return E_NOTIMPL;
1168 }
1169
1170 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1171         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1172 {
1173     FIXME("\n");
1174     return E_NOTIMPL;
1175 }
1176
1177 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1178         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1179 {
1180     StringInstance *This = (StringInstance*)dispex;
1181
1182     TRACE("\n");
1183
1184     switch(flags) {
1185     case DISPATCH_PROPERTYGET: {
1186         BSTR str = SysAllocString(This->str);
1187         if(!str)
1188             return E_OUTOFMEMORY;
1189
1190         V_VT(retv) = VT_BSTR;
1191         V_BSTR(retv) = str;
1192         break;
1193     }
1194     default:
1195         FIXME("flags %x\n", flags);
1196         return E_NOTIMPL;
1197     }
1198
1199     return S_OK;
1200 }
1201
1202 static void String_destructor(DispatchEx *dispex)
1203 {
1204     StringInstance *This = (StringInstance*)dispex;
1205
1206     heap_free(This->str);
1207     heap_free(This);
1208 }
1209
1210 static const builtin_prop_t String_props[] = {
1211     {anchorW,                String_anchor,                PROPF_METHOD},
1212     {bigW,                   String_big,                   PROPF_METHOD},
1213     {blinkW,                 String_blink,                 PROPF_METHOD},
1214     {boldW,                  String_bold,                  PROPF_METHOD},
1215     {charAtW,                String_charAt,                PROPF_METHOD},
1216     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
1217     {concatW,                String_concat,                PROPF_METHOD},
1218     {fixedW,                 String_fixed,                 PROPF_METHOD},
1219     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
1220     {fontsizeW,              String_fontsize,              PROPF_METHOD},
1221     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
1222     {indexOfW,               String_indexOf,               PROPF_METHOD},
1223     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
1224     {italicsW,               String_italics,               PROPF_METHOD},
1225     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
1226     {lengthW,                String_length,                0},
1227     {linkW,                  String_link,                  PROPF_METHOD},
1228     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
1229     {matchW,                 String_match,                 PROPF_METHOD},
1230     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1231     {replaceW,               String_replace,               PROPF_METHOD},
1232     {searchW,                String_search,                PROPF_METHOD},
1233     {sliceW,                 String_slice,                 PROPF_METHOD},
1234     {smallW,                 String_small,                 PROPF_METHOD},
1235     {splitW,                 String_split,                 PROPF_METHOD},
1236     {strikeW,                String_strike,                PROPF_METHOD},
1237     {substringW,             String_substring,             PROPF_METHOD},
1238     {substrW,                String_substr,                PROPF_METHOD},
1239     {subW,                   String_sub,                   PROPF_METHOD},
1240     {supW,                   String_sup,                   PROPF_METHOD},
1241     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1242     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1243     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1244     {toStringW,              String_toString,              PROPF_METHOD},
1245     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1246     {valueOfW,               String_valueOf,               PROPF_METHOD}
1247 };
1248
1249 static const builtin_info_t String_info = {
1250     JSCLASS_STRING,
1251     {NULL, String_value, 0},
1252     sizeof(String_props)/sizeof(*String_props),
1253     String_props,
1254     String_destructor,
1255     NULL
1256 };
1257
1258 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1259         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1260 {
1261     HRESULT hres;
1262
1263     TRACE("\n");
1264
1265     switch(flags) {
1266     case INVOKE_FUNC: {
1267         BSTR str;
1268
1269         if(arg_cnt(dp)) {
1270             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1271             if(FAILED(hres))
1272                 return hres;
1273         }else {
1274             str = SysAllocStringLen(NULL, 0);
1275             if(!str)
1276                 return E_OUTOFMEMORY;
1277         }
1278
1279         V_VT(retv) = VT_BSTR;
1280         V_BSTR(retv) = str;
1281         break;
1282     }
1283     case DISPATCH_CONSTRUCT: {
1284         DispatchEx *ret;
1285
1286         if(arg_cnt(dp)) {
1287             BSTR str;
1288
1289             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1290             if(FAILED(hres))
1291                 return hres;
1292
1293             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1294             SysFreeString(str);
1295         }else {
1296             hres = create_string(dispex->ctx, NULL, 0, &ret);
1297         }
1298
1299         if(FAILED(hres))
1300             return hres;
1301
1302         V_VT(retv) = VT_DISPATCH;
1303         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1304         break;
1305     }
1306
1307     default:
1308         FIXME("unimplemented flags: %x\n", flags);
1309         return E_NOTIMPL;
1310     }
1311
1312     return S_OK;
1313 }
1314
1315 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1316 {
1317     StringInstance *string;
1318     HRESULT hres;
1319
1320     string = heap_alloc_zero(sizeof(StringInstance));
1321     if(!string)
1322         return E_OUTOFMEMORY;
1323
1324     if(use_constr)
1325         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1326     else
1327         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1328     if(FAILED(hres)) {
1329         heap_free(string);
1330         return hres;
1331     }
1332
1333     *ret = string;
1334     return S_OK;
1335 }
1336
1337 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1338 {
1339     StringInstance *string;
1340     HRESULT hres;
1341
1342     hres = string_alloc(ctx, FALSE, &string);
1343     if(FAILED(hres))
1344         return hres;
1345
1346     hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret);
1347
1348     jsdisp_release(&string->dispex);
1349     return hres;
1350 }
1351
1352 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1353 {
1354     StringInstance *string;
1355     HRESULT hres;
1356
1357     hres = string_alloc(ctx, TRUE, &string);
1358     if(FAILED(hres))
1359         return hres;
1360
1361     if(len == -1)
1362         len = strlenW(str);
1363
1364     string->length = len;
1365     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1366     if(!string->str) {
1367         jsdisp_release(&string->dispex);
1368         return E_OUTOFMEMORY;
1369     }
1370
1371     memcpy(string->str, str, len*sizeof(WCHAR));
1372     string->str[len] = 0;
1373
1374     *ret = &string->dispex;
1375     return S_OK;
1376
1377 }