jscript: Fixed crash in run test.
[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 = 0, 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     StringInstance *string;
789     match_result_t *match_result;
790     DWORD match_cnt, i, len;
791     const WCHAR *ptr;
792     VARIANT *arg, var;
793     DispatchEx *array;
794     HRESULT hres;
795
796     TRACE("\n");
797
798     if(!is_class(dispex, JSCLASS_STRING)) {
799         FIXME("not String this\n");
800         return E_NOTIMPL;
801     }
802
803     string = (StringInstance*)dispex;
804
805     if(arg_cnt(dp) != 1) {
806         FIXME("unsupported args\n");
807         return E_NOTIMPL;
808     }
809
810     arg = get_arg(dp, 0);
811     switch(V_VT(arg)) {
812     case VT_DISPATCH: {
813         DispatchEx *regexp;
814
815         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
816         if(regexp) {
817             if(is_class(regexp, JSCLASS_REGEXP)) {
818                 hres = regexp_match(regexp, string->str, string->length, TRUE, &match_result, &match_cnt);
819                 jsdisp_release(regexp);
820                 if(FAILED(hres))
821                     return hres;
822                 break;
823             }
824             jsdisp_release(regexp);
825         }
826     }
827     default:
828         FIXME("unsupported vt %d\n", V_VT(arg));
829         return E_NOTIMPL;
830     }
831
832     hres = create_array(dispex->ctx, match_cnt+1, &array);
833
834     if(SUCCEEDED(hres)) {
835         ptr = string->str;
836         for(i=0; i < match_cnt; i++) {
837             len = match_result[i].str-ptr;
838             V_VT(&var) = VT_BSTR;
839             V_BSTR(&var) = SysAllocStringLen(ptr, len);
840             if(!V_BSTR(&var)) {
841                 hres = E_OUTOFMEMORY;
842                 break;
843             }
844
845             hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
846             SysFreeString(V_BSTR(&var));
847             if(FAILED(hres))
848                 break;
849
850             ptr = match_result[i].str + match_result[i].len;
851         }
852     }
853
854     if(SUCCEEDED(hres)) {
855         len = (string->str+string->length) - ptr;
856
857         V_VT(&var) = VT_BSTR;
858         V_BSTR(&var) = SysAllocStringLen(ptr, len);
859
860         hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
861         SysFreeString(V_BSTR(&var));
862     }
863
864     heap_free(match_result);
865
866     if(SUCCEEDED(hres) && retv) {
867         V_VT(retv) = VT_DISPATCH;
868         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
869     }else {
870         jsdisp_release(array);
871     }
872
873     return hres;
874 }
875
876 static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
877         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
878 {
879     FIXME("\n");
880     return E_NOTIMPL;
881 }
882
883 static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
884         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
885 {
886     FIXME("\n");
887     return E_NOTIMPL;
888 }
889
890 /* ECMA-262 3rd Edition    15.5.4.15 */
891 static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
892         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
893 {
894     const WCHAR *str;
895     INT start=0, end;
896     DWORD length;
897     VARIANT v;
898     HRESULT hres;
899
900     TRACE("\n");
901
902     if(is_class(dispex, JSCLASS_STRING)) {
903         StringInstance *string = (StringInstance*)dispex;
904
905         length = string->length;
906         str = string->str;
907     }else {
908         FIXME("not string this not supported\n");
909         return E_NOTIMPL;
910     }
911
912     if(arg_cnt(dp) >= 1) {
913         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
914         if(FAILED(hres))
915             return hres;
916
917         if(V_VT(&v) == VT_I4) {
918             start = V_I4(&v);
919             if(start < 0)
920                 start = 0;
921             else if(start >= length)
922                 start = length;
923         }else {
924             start = V_R8(&v) < 0.0 ? 0 : length;
925         }
926     }
927
928     if(arg_cnt(dp) >= 2) {
929         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
930         if(FAILED(hres))
931             return hres;
932
933         if(V_VT(&v) == VT_I4) {
934             end = V_I4(&v);
935             if(end < 0)
936                 end = 0;
937             else if(end > length)
938                 end = length;
939         }else {
940             end = V_R8(&v) < 0.0 ? 0 : length;
941         }
942     }else {
943         end = length;
944     }
945
946     if(start > end) {
947         INT tmp = start;
948         start = end;
949         end = tmp;
950     }
951
952     if(retv) {
953         V_VT(retv) = VT_BSTR;
954         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
955         if(!V_BSTR(retv))
956             return E_OUTOFMEMORY;
957     }
958     return S_OK;
959 }
960
961 static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
962         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
963 {
964     FIXME("\n");
965     return E_NOTIMPL;
966 }
967
968 static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
969         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
970 {
971     FIXME("\n");
972     return E_NOTIMPL;
973 }
974
975 static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
976         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
977 {
978     FIXME("\n");
979     return E_NOTIMPL;
980 }
981
982 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
983         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
984 {
985     FIXME("\n");
986     return E_NOTIMPL;
987 }
988
989 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
990         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
991 {
992     FIXME("\n");
993     return E_NOTIMPL;
994 }
995
996 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
997         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
998 {
999     FIXME("\n");
1000     return E_NOTIMPL;
1001 }
1002
1003 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1004         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1005 {
1006     FIXME("\n");
1007     return E_NOTIMPL;
1008 }
1009
1010 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1011         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1012 {
1013     FIXME("\n");
1014     return E_NOTIMPL;
1015 }
1016
1017 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1018         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1019 {
1020     FIXME("\n");
1021     return E_NOTIMPL;
1022 }
1023
1024 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1025         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1026 {
1027     FIXME("\n");
1028     return E_NOTIMPL;
1029 }
1030
1031 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1032         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1033 {
1034     StringInstance *This = (StringInstance*)dispex;
1035
1036     TRACE("\n");
1037
1038     switch(flags) {
1039     case DISPATCH_PROPERTYGET: {
1040         BSTR str = SysAllocString(This->str);
1041         if(!str)
1042             return E_OUTOFMEMORY;
1043
1044         V_VT(retv) = VT_BSTR;
1045         V_BSTR(retv) = str;
1046         break;
1047     }
1048     default:
1049         FIXME("flags %x\n", flags);
1050         return E_NOTIMPL;
1051     }
1052
1053     return S_OK;
1054 }
1055
1056 static void String_destructor(DispatchEx *dispex)
1057 {
1058     StringInstance *This = (StringInstance*)dispex;
1059
1060     heap_free(This->str);
1061     heap_free(This);
1062 }
1063
1064 static const builtin_prop_t String_props[] = {
1065     {anchorW,                String_anchor,                PROPF_METHOD},
1066     {bigW,                   String_big,                   PROPF_METHOD},
1067     {blinkW,                 String_blink,                 PROPF_METHOD},
1068     {boldW,                  String_bold,                  PROPF_METHOD},
1069     {charAtW,                String_charAt,                PROPF_METHOD},
1070     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
1071     {concatW,                String_concat,                PROPF_METHOD},
1072     {fixedW,                 String_fixed,                 PROPF_METHOD},
1073     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
1074     {fontsizeW,              String_fontsize,              PROPF_METHOD},
1075     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
1076     {indexOfW,               String_indexOf,               PROPF_METHOD},
1077     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
1078     {italicsW,               String_italics,               PROPF_METHOD},
1079     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
1080     {lengthW,                String_length,                0},
1081     {linkW,                  String_link,                  PROPF_METHOD},
1082     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
1083     {matchW,                 String_match,                 PROPF_METHOD},
1084     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1085     {replaceW,               String_replace,               PROPF_METHOD},
1086     {searchW,                String_search,                PROPF_METHOD},
1087     {sliceW,                 String_slice,                 PROPF_METHOD},
1088     {smallW,                 String_small,                 PROPF_METHOD},
1089     {splitW,                 String_split,                 PROPF_METHOD},
1090     {strikeW,                String_strike,                PROPF_METHOD},
1091     {substringW,             String_substring,             PROPF_METHOD},
1092     {substrW,                String_substr,                PROPF_METHOD},
1093     {subW,                   String_sub,                   PROPF_METHOD},
1094     {supW,                   String_sup,                   PROPF_METHOD},
1095     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1096     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1097     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1098     {toStringW,              String_toString,              PROPF_METHOD},
1099     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1100     {valueOfW,               String_valueOf,               PROPF_METHOD}
1101 };
1102
1103 static const builtin_info_t String_info = {
1104     JSCLASS_STRING,
1105     {NULL, String_value, 0},
1106     sizeof(String_props)/sizeof(*String_props),
1107     String_props,
1108     String_destructor,
1109     NULL
1110 };
1111
1112 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1113         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1114 {
1115     HRESULT hres;
1116
1117     TRACE("\n");
1118
1119     switch(flags) {
1120     case INVOKE_FUNC: {
1121         BSTR str;
1122
1123         if(arg_cnt(dp)) {
1124             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1125             if(FAILED(hres))
1126                 return hres;
1127         }else {
1128             str = SysAllocStringLen(NULL, 0);
1129             if(!str)
1130                 return E_OUTOFMEMORY;
1131         }
1132
1133         V_VT(retv) = VT_BSTR;
1134         V_BSTR(retv) = str;
1135         break;
1136     }
1137     case DISPATCH_CONSTRUCT: {
1138         DispatchEx *ret;
1139
1140         if(arg_cnt(dp)) {
1141             BSTR str;
1142
1143             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1144             if(FAILED(hres))
1145                 return hres;
1146
1147             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1148             SysFreeString(str);
1149         }else {
1150             hres = create_string(dispex->ctx, NULL, 0, &ret);
1151         }
1152
1153         if(FAILED(hres))
1154             return hres;
1155
1156         V_VT(retv) = VT_DISPATCH;
1157         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1158         break;
1159     }
1160
1161     default:
1162         FIXME("unimplemented flags: %x\n", flags);
1163         return E_NOTIMPL;
1164     }
1165
1166     return S_OK;
1167 }
1168
1169 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1170 {
1171     StringInstance *string;
1172     HRESULT hres;
1173
1174     string = heap_alloc_zero(sizeof(StringInstance));
1175     if(!string)
1176         return E_OUTOFMEMORY;
1177
1178     if(use_constr)
1179         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1180     else
1181         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1182     if(FAILED(hres)) {
1183         heap_free(string);
1184         return hres;
1185     }
1186
1187     *ret = string;
1188     return S_OK;
1189 }
1190
1191 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1192 {
1193     StringInstance *string;
1194     HRESULT hres;
1195
1196     hres = string_alloc(ctx, FALSE, &string);
1197     if(FAILED(hres))
1198         return hres;
1199
1200     hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret);
1201
1202     jsdisp_release(&string->dispex);
1203     return hres;
1204 }
1205
1206 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1207 {
1208     StringInstance *string;
1209     HRESULT hres;
1210
1211     hres = string_alloc(ctx, TRUE, &string);
1212     if(FAILED(hres))
1213         return hres;
1214
1215     if(len == -1)
1216         len = strlenW(str);
1217
1218     string->length = len;
1219     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1220     if(!string->str) {
1221         jsdisp_release(&string->dispex);
1222         return E_OUTOFMEMORY;
1223     }
1224
1225     memcpy(string->str, str, len*sizeof(WCHAR));
1226     string->str[len] = 0;
1227
1228     *ret = &string->dispex;
1229     return S_OK;
1230
1231 }