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