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