jscript: Added String.match implementation for non-regexp arguments.
[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     FIXME("\n");
327     return E_NOTIMPL;
328 }
329
330 static HRESULT String_italics(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
331         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
332 {
333     FIXME("\n");
334     return E_NOTIMPL;
335 }
336
337 static HRESULT String_lastIndexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
338         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
339 {
340     FIXME("\n");
341     return E_NOTIMPL;
342 }
343
344 static HRESULT String_link(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
345         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
346 {
347     FIXME("\n");
348     return E_NOTIMPL;
349 }
350
351 /* ECMA-262 3rd Edition    15.5.4.10 */
352 static HRESULT String_match(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
353         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
354 {
355     StringInstance *This = (StringInstance*)dispex;
356     match_result_t *match_result;
357     DispatchEx *regexp;
358     DispatchEx *array;
359     VARIANT var, *arg_var;
360     DWORD match_cnt, i;
361     HRESULT hres = S_OK;
362
363     TRACE("\n");
364
365     if(arg_cnt(dp) != 1) {
366         FIXME("unsupported args\n");
367         return E_NOTIMPL;
368     }
369
370     arg_var = get_arg(dp, 0);
371     switch(V_VT(arg_var)) {
372     case VT_DISPATCH:
373         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
374         if(regexp) {
375             if(regexp->builtin_info->class == JSCLASS_REGEXP)
376                 break;
377             jsdisp_release(regexp);
378         }
379     default: {
380         BSTR match_str;
381
382         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
383         if(FAILED(hres))
384             return hres;
385
386         hres = create_regexp_str(dispex->ctx, match_str, SysStringLen(match_str), NULL, 0, &regexp);
387         SysFreeString(match_str);
388         if(FAILED(hres))
389             return hres;
390     }
391     }
392
393     hres = regexp_match(regexp, This->str, This->length, FALSE, &match_result, &match_cnt);
394     jsdisp_release(regexp);
395     if(FAILED(hres))
396         return hres;
397
398     if(!match_cnt) {
399         TRACE("no match\n");
400
401         if(retv)
402             V_VT(retv) = VT_NULL;
403         return S_OK;
404     }
405
406     hres = create_array(dispex->ctx, match_cnt, &array);
407     if(FAILED(hres))
408         return hres;
409
410     V_VT(&var) = VT_BSTR;
411
412     for(i=0; i < match_cnt; i++) {
413         V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len);
414         if(!V_BSTR(&var)) {
415             hres = E_OUTOFMEMORY;
416             break;
417         }
418
419         hres = jsdisp_propput_idx(array, i, lcid, &var, ei, NULL/*FIXME*/);
420         SysFreeString(V_BSTR(&var));
421         if(FAILED(hres))
422             break;
423     }
424
425     if(SUCCEEDED(hres) && retv) {
426         V_VT(retv) = VT_DISPATCH;
427         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
428     }else {
429         jsdisp_release(array);
430     }
431     return hres;
432 }
433
434 typedef struct {
435     WCHAR *buf;
436     DWORD size;
437     DWORD len;
438 } strbuf_t;
439
440 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
441 {
442     if(!len)
443         return S_OK;
444
445     if(len + buf->len > buf->size) {
446         WCHAR *new_buf;
447         DWORD new_size;
448
449         new_size = buf->size ? buf->size<<1 : 16;
450         if(new_size < buf->len+len)
451             new_size = buf->len+len;
452         if(buf->buf)
453             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
454         else
455             new_buf = heap_alloc(new_size*sizeof(WCHAR));
456         if(!new_buf)
457             return E_OUTOFMEMORY;
458
459         buf->buf = new_buf;
460         buf->size = new_size;
461     }
462
463     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
464     buf->len += len;
465     return S_OK;
466 }
467
468 static HRESULT rep_call(DispatchEx *func, const WCHAR *str, match_result_t *match, match_result_t *parens,
469         DWORD parens_cnt, LCID lcid, BSTR *ret, jsexcept_t *ei, IServiceProvider *caller)
470 {
471     DISPPARAMS dp = {NULL, NULL, 0, 0};
472     VARIANTARG *args, *arg;
473     VARIANT var;
474     DWORD i;
475     HRESULT hres = S_OK;
476
477     dp.cArgs = parens_cnt+3;
478     dp.rgvarg = args = heap_alloc_zero(sizeof(VARIANT)*dp.cArgs);
479     if(!args)
480         return E_OUTOFMEMORY;
481
482     arg = get_arg(&dp,0);
483     V_VT(arg) = VT_BSTR;
484     V_BSTR(arg) = SysAllocStringLen(match->str, match->len);
485     if(!V_BSTR(arg))
486         hres = E_OUTOFMEMORY;
487
488     if(SUCCEEDED(hres)) {
489         for(i=0; i < parens_cnt; i++) {
490             arg = get_arg(&dp,i+1);
491             V_VT(arg) = VT_BSTR;
492             V_BSTR(arg) = SysAllocStringLen(parens[i].str, parens[i].len);
493             if(!V_BSTR(arg)) {
494                hres = E_OUTOFMEMORY;
495                break;
496             }
497         }
498     }
499
500     if(SUCCEEDED(hres)) {
501         arg = get_arg(&dp,parens_cnt+1);
502         V_VT(arg) = VT_I4;
503         V_I4(arg) = match->str - str;
504
505         arg = get_arg(&dp,parens_cnt+2);
506         V_VT(arg) = VT_BSTR;
507         V_BSTR(arg) = SysAllocString(str);
508         if(!V_BSTR(arg))
509             hres = E_OUTOFMEMORY;
510     }
511
512     if(SUCCEEDED(hres))
513         hres = jsdisp_call_value(func, lcid, DISPATCH_METHOD, &dp, &var, ei, caller);
514
515     for(i=0; i < parens_cnt+1; i++) {
516         if(i != parens_cnt+1)
517             SysFreeString(V_BSTR(get_arg(&dp,i)));
518     }
519     heap_free(args);
520
521     if(FAILED(hres))
522         return hres;
523
524     hres = to_string(func->ctx, &var, ei, ret);
525     VariantClear(&var);
526     return hres;
527 }
528
529 /* ECMA-262 3rd Edition    15.5.4.11 */
530 static HRESULT String_replace(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
531         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
532 {
533     DWORD parens_cnt, parens_size=0, rep_len=0, length;
534     BSTR rep_str = NULL, match_str = NULL, ret_str;
535     DispatchEx *rep_func = NULL, *regexp = NULL;
536     match_result_t *parens = NULL, match;
537     const WCHAR *str;
538     strbuf_t ret = {NULL,0,0};
539     BOOL gcheck = FALSE;
540     VARIANT *arg_var;
541     HRESULT hres = S_OK;
542
543     TRACE("\n");
544
545     if(is_class(dispex, JSCLASS_STRING)) {
546         StringInstance *string = (StringInstance*)dispex;
547         str = string->str;
548         length = string->length;
549     }else {
550         FIXME("not String this\n");
551         return E_NOTIMPL;
552     }
553
554     if(!arg_cnt(dp)) {
555         if(retv) {
556             ret_str = SysAllocString(str);
557             if(!ret_str)
558                 return E_OUTOFMEMORY;
559
560             V_VT(retv) = VT_BSTR;
561             V_BSTR(retv) = ret_str;
562         }
563         return S_OK;
564     }
565
566     arg_var = get_arg(dp, 0);
567     switch(V_VT(arg_var)) {
568     case VT_DISPATCH:
569         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
570         if(regexp) {
571             if(is_class(regexp, JSCLASS_REGEXP)) {
572                 break;
573             }else {
574                 jsdisp_release(regexp);
575                 regexp = NULL;
576             }
577         }
578
579     default:
580         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
581         if(FAILED(hres))
582             return hres;
583     }
584
585     if(arg_cnt(dp) >= 2) {
586         arg_var = get_arg(dp,1);
587         switch(V_VT(arg_var)) {
588         case VT_DISPATCH:
589             rep_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
590             if(rep_func) {
591                 if(is_class(rep_func, JSCLASS_FUNCTION)) {
592                     break;
593                 }else {
594                     jsdisp_release(rep_func);
595                     rep_func = NULL;
596                 }
597             }
598
599         default:
600             hres = to_string(dispex->ctx, arg_var, ei, &rep_str);
601             if(FAILED(hres))
602                 break;
603
604             if(strchrW(rep_str, '$')) {
605                 FIXME("unsupported $ in replace string\n");
606                 hres = E_NOTIMPL;
607             }
608
609             rep_len = SysStringLen(rep_str);
610         }
611     }
612
613     if(SUCCEEDED(hres)) {
614         const WCHAR *cp, *ecp;
615
616         cp = ecp = str;
617
618         while(1) {
619             if(regexp) {
620                 hres = regexp_match_next(regexp, gcheck, str, length, &cp, rep_func ? &parens : NULL,
621                                          &parens_size, &parens_cnt, &match);
622                 gcheck = TRUE;
623
624                 if(hres == S_FALSE) {
625                     hres = S_OK;
626                     break;
627                 }
628                 if(FAILED(hres))
629                     break;
630             }else {
631                 match.str = strstrW(cp, match_str);
632                 if(!match.str)
633                     break;
634                 match.len = SysStringLen(match_str);
635                 cp = match.str+match.len;
636             }
637
638             hres = strbuf_append(&ret, ecp, match.str-ecp);
639             ecp = match.str+match.len;
640             if(FAILED(hres))
641                 break;
642
643             if(rep_func) {
644                 BSTR cstr;
645
646                 hres = rep_call(rep_func, str, &match, parens, parens_cnt, lcid, &cstr, ei, caller);
647                 if(FAILED(hres))
648                     break;
649
650                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
651                 SysFreeString(cstr);
652                 if(FAILED(hres))
653                     break;
654             }else if(rep_str) {
655                 hres = strbuf_append(&ret, rep_str, rep_len);
656                 if(FAILED(hres))
657                     break;
658             }else {
659                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
660
661                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
662                 if(FAILED(hres))
663                     break;
664             }
665         }
666
667         if(SUCCEEDED(hres))
668             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
669     }
670
671     if(rep_func)
672         jsdisp_release(rep_func);
673     if(regexp)
674         jsdisp_release(regexp);
675     SysFreeString(rep_str);
676     SysFreeString(match_str);
677     heap_free(parens);
678
679     if(SUCCEEDED(hres) && retv) {
680         ret_str = SysAllocStringLen(ret.buf, ret.len);
681         if(!ret_str)
682             return E_OUTOFMEMORY;
683
684         V_VT(retv) = VT_BSTR;
685         V_BSTR(retv) = ret_str;
686         TRACE("= %s\n", debugstr_w(ret_str));
687     }
688
689     heap_free(ret.buf);
690     return hres;
691 }
692
693 static HRESULT String_search(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
694         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
695 {
696     FIXME("\n");
697     return E_NOTIMPL;
698 }
699
700 /* ECMA-262 3rd Edition    15.5.4.13 */
701 static HRESULT String_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
702         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
703 {
704     const WCHAR *str;
705     DWORD length;
706     INT start=0, end;
707     VARIANT v;
708     HRESULT hres;
709
710     TRACE("\n");
711
712     if(is_class(dispex, JSCLASS_STRING)) {
713         StringInstance *string = (StringInstance*)dispex;
714
715         str = string->str;
716         length = string->length;
717     }else {
718         FIXME("this is not a string class\n");
719         return E_NOTIMPL;
720     }
721
722     if(arg_cnt(dp)) {
723         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
724         if(FAILED(hres))
725             return hres;
726
727         if(V_VT(&v) == VT_I4) {
728             start = V_I4(&v);
729             if(start < 0) {
730                 start = length + start;
731                 if(start < 0)
732                     start = 0;
733             }else if(start > length) {
734                 start = length;
735             }
736         }else {
737             start = V_R8(&v) < 0.0 ? 0 : length;
738         }
739     }else {
740         start = 0;
741     }
742
743     if(arg_cnt(dp) >= 2) {
744         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
745         if(FAILED(hres))
746             return hres;
747
748         if(V_VT(&v) == VT_I4) {
749             end = V_I4(&v);
750             if(end < 0) {
751                 end = length + end;
752                 if(end < 0)
753                     end = 0;
754             }else if(end > length) {
755                 end = length;
756             }
757         }else {
758             end = V_R8(&v) < 0.0 ? 0 : length;
759         }
760     }else {
761         end = length;
762     }
763
764     if(end < start)
765         end = start;
766
767     if(retv) {
768         BSTR retstr = SysAllocStringLen(str+start, end-start);
769         if(!str)
770             return E_OUTOFMEMORY;
771
772         V_VT(retv) = VT_BSTR;
773         V_BSTR(retv) = retstr;
774     }
775     return S_OK;
776 }
777
778 static HRESULT String_small(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
779         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
780 {
781     FIXME("\n");
782     return E_NOTIMPL;
783 }
784
785 static HRESULT String_split(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
786         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
787 {
788     FIXME("\n");
789     return E_NOTIMPL;
790 }
791
792 static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
793         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
794 {
795     FIXME("\n");
796     return E_NOTIMPL;
797 }
798
799 static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
800         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
801 {
802     FIXME("\n");
803     return E_NOTIMPL;
804 }
805
806 /* ECMA-262 3rd Edition    15.5.4.15 */
807 static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
808         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
809 {
810     const WCHAR *str;
811     INT start=0, end;
812     DWORD length;
813     VARIANT v;
814     HRESULT hres;
815
816     TRACE("\n");
817
818     if(is_class(dispex, JSCLASS_STRING)) {
819         StringInstance *string = (StringInstance*)dispex;
820
821         length = string->length;
822         str = string->str;
823     }else {
824         FIXME("not string this not supported\n");
825         return E_NOTIMPL;
826     }
827
828     if(arg_cnt(dp) >= 1) {
829         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
830         if(FAILED(hres))
831             return hres;
832
833         if(V_VT(&v) == VT_I4) {
834             start = V_I4(&v);
835             if(start < 0)
836                 start = 0;
837             else if(start >= length)
838                 start = length;
839         }else {
840             start = V_R8(&v) < 0.0 ? 0 : length;
841         }
842     }
843
844     if(arg_cnt(dp) >= 2) {
845         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
846         if(FAILED(hres))
847             return hres;
848
849         if(V_VT(&v) == VT_I4) {
850             end = V_I4(&v);
851             if(end < 0)
852                 end = 0;
853             else if(end > length)
854                 end = length;
855         }else {
856             end = V_R8(&v) < 0.0 ? 0 : length;
857         }
858     }else {
859         end = length;
860     }
861
862     if(start > end) {
863         INT tmp = start;
864         start = end;
865         end = tmp;
866     }
867
868     if(retv) {
869         V_VT(retv) = VT_BSTR;
870         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
871         if(!V_BSTR(retv))
872             return E_OUTOFMEMORY;
873     }
874     return S_OK;
875 }
876
877 static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
878         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
879 {
880     FIXME("\n");
881     return E_NOTIMPL;
882 }
883
884 static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
885         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
886 {
887     FIXME("\n");
888     return E_NOTIMPL;
889 }
890
891 static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
892         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
893 {
894     FIXME("\n");
895     return E_NOTIMPL;
896 }
897
898 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
899         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
900 {
901     FIXME("\n");
902     return E_NOTIMPL;
903 }
904
905 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
906         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
907 {
908     FIXME("\n");
909     return E_NOTIMPL;
910 }
911
912 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
913         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
914 {
915     FIXME("\n");
916     return E_NOTIMPL;
917 }
918
919 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
920         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
921 {
922     FIXME("\n");
923     return E_NOTIMPL;
924 }
925
926 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
927         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
928 {
929     FIXME("\n");
930     return E_NOTIMPL;
931 }
932
933 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
934         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
935 {
936     FIXME("\n");
937     return E_NOTIMPL;
938 }
939
940 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
941         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
942 {
943     FIXME("\n");
944     return E_NOTIMPL;
945 }
946
947 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
948         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
949 {
950     StringInstance *This = (StringInstance*)dispex;
951
952     TRACE("\n");
953
954     switch(flags) {
955     case DISPATCH_PROPERTYGET: {
956         BSTR str = SysAllocString(This->str);
957         if(!str)
958             return E_OUTOFMEMORY;
959
960         V_VT(retv) = VT_BSTR;
961         V_BSTR(retv) = str;
962         break;
963     }
964     default:
965         FIXME("flags %x\n", flags);
966         return E_NOTIMPL;
967     }
968
969     return S_OK;
970 }
971
972 static void String_destructor(DispatchEx *dispex)
973 {
974     StringInstance *This = (StringInstance*)dispex;
975
976     heap_free(This->str);
977     heap_free(This);
978 }
979
980 static const builtin_prop_t String_props[] = {
981     {anchorW,                String_anchor,                PROPF_METHOD},
982     {bigW,                   String_big,                   PROPF_METHOD},
983     {blinkW,                 String_blink,                 PROPF_METHOD},
984     {boldW,                  String_bold,                  PROPF_METHOD},
985     {charAtW,                String_charAt,                PROPF_METHOD},
986     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
987     {concatW,                String_concat,                PROPF_METHOD},
988     {fixedW,                 String_fixed,                 PROPF_METHOD},
989     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
990     {fontsizeW,              String_fontsize,              PROPF_METHOD},
991     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
992     {indexOfW,               String_indexOf,               PROPF_METHOD},
993     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
994     {italicsW,               String_italics,               PROPF_METHOD},
995     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
996     {lengthW,                String_length,                0},
997     {linkW,                  String_link,                  PROPF_METHOD},
998     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
999     {matchW,                 String_match,                 PROPF_METHOD},
1000     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1001     {replaceW,               String_replace,               PROPF_METHOD},
1002     {searchW,                String_search,                PROPF_METHOD},
1003     {sliceW,                 String_slice,                 PROPF_METHOD},
1004     {smallW,                 String_small,                 PROPF_METHOD},
1005     {splitW,                 String_split,                 PROPF_METHOD},
1006     {strikeW,                String_strike,                PROPF_METHOD},
1007     {substringW,             String_substring,             PROPF_METHOD},
1008     {substrW,                String_substr,                PROPF_METHOD},
1009     {subW,                   String_sub,                   PROPF_METHOD},
1010     {supW,                   String_sup,                   PROPF_METHOD},
1011     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1012     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1013     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1014     {toStringW,              String_toString,              PROPF_METHOD},
1015     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1016     {valueOfW,               String_valueOf,               PROPF_METHOD}
1017 };
1018
1019 static const builtin_info_t String_info = {
1020     JSCLASS_STRING,
1021     {NULL, String_value, 0},
1022     sizeof(String_props)/sizeof(*String_props),
1023     String_props,
1024     String_destructor,
1025     NULL
1026 };
1027
1028 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1029         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1030 {
1031     HRESULT hres;
1032
1033     TRACE("\n");
1034
1035     switch(flags) {
1036     case INVOKE_FUNC: {
1037         BSTR str;
1038
1039         if(arg_cnt(dp)) {
1040             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1041             if(FAILED(hres))
1042                 return hres;
1043         }else {
1044             str = SysAllocStringLen(NULL, 0);
1045             if(!str)
1046                 return E_OUTOFMEMORY;
1047         }
1048
1049         V_VT(retv) = VT_BSTR;
1050         V_BSTR(retv) = str;
1051         break;
1052     }
1053     case DISPATCH_CONSTRUCT: {
1054         DispatchEx *ret;
1055
1056         if(arg_cnt(dp)) {
1057             BSTR str;
1058
1059             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1060             if(FAILED(hres))
1061                 return hres;
1062
1063             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1064             SysFreeString(str);
1065         }else {
1066             hres = create_string(dispex->ctx, NULL, 0, &ret);
1067         }
1068
1069         if(FAILED(hres))
1070             return hres;
1071
1072         V_VT(retv) = VT_DISPATCH;
1073         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1074         break;
1075     }
1076
1077     default:
1078         FIXME("unimplemented flags: %x\n", flags);
1079         return E_NOTIMPL;
1080     }
1081
1082     return S_OK;
1083 }
1084
1085 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1086 {
1087     StringInstance *string;
1088     HRESULT hres;
1089
1090     string = heap_alloc_zero(sizeof(StringInstance));
1091     if(!string)
1092         return E_OUTOFMEMORY;
1093
1094     if(use_constr)
1095         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1096     else
1097         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1098     if(FAILED(hres)) {
1099         heap_free(string);
1100         return hres;
1101     }
1102
1103     *ret = string;
1104     return S_OK;
1105 }
1106
1107 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1108 {
1109     StringInstance *string;
1110     HRESULT hres;
1111
1112     hres = string_alloc(ctx, FALSE, &string);
1113     if(FAILED(hres))
1114         return hres;
1115
1116     hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret);
1117
1118     jsdisp_release(&string->dispex);
1119     return hres;
1120 }
1121
1122 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1123 {
1124     StringInstance *string;
1125     HRESULT hres;
1126
1127     hres = string_alloc(ctx, TRUE, &string);
1128     if(FAILED(hres))
1129         return hres;
1130
1131     if(len == -1)
1132         len = strlenW(str);
1133
1134     string->length = len;
1135     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1136     if(!string->str) {
1137         jsdisp_release(&string->dispex);
1138         return E_OUTOFMEMORY;
1139     }
1140
1141     memcpy(string->str, str, len*sizeof(WCHAR));
1142     string->str[len] = 0;
1143
1144     *ret = &string->dispex;
1145     return S_OK;
1146
1147 }