dbghelp: Properly fail on PDB files generated by MSVC compiler version 14.31.
[wine.git] / dlls / jscript / string.c
blobdac89c9d8a96f9037fb3e50e19d6904cdb50c90b
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
20 #include <math.h>
22 #include "jscript.h"
23 #include "regexp.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 jsdisp_t dispex;
31 jsstr_t *str;
32 } StringInstance;
34 static inline StringInstance *string_from_jsdisp(jsdisp_t *jsdisp)
36 return CONTAINING_RECORD(jsdisp, StringInstance, dispex);
39 static inline StringInstance *string_this(jsval_t vthis)
41 jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL;
42 return (jsdisp && is_class(jsdisp, JSCLASS_STRING)) ? string_from_jsdisp(jsdisp) : NULL;
45 static HRESULT get_string_val(script_ctx_t *ctx, jsval_t vthis, jsstr_t **val)
47 StringInstance *string;
49 if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5 && (is_undefined(vthis) || is_null(vthis)))
50 return JS_E_OBJECT_EXPECTED;
52 if((string = string_this(vthis))) {
53 *val = jsstr_addref(string->str);
54 return S_OK;
57 return to_string(ctx, vthis, val);
60 static HRESULT get_string_flat_val(script_ctx_t *ctx, jsval_t vthis, jsstr_t **jsval, const WCHAR **val)
62 HRESULT hres;
64 hres = get_string_val(ctx, vthis, jsval);
65 if(FAILED(hres))
66 return hres;
68 *val = jsstr_flatten(*jsval);
69 if(*val)
70 return S_OK;
72 jsstr_release(*jsval);
73 return E_OUTOFMEMORY;
76 static HRESULT String_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
78 StringInstance *string = string_from_jsdisp(jsthis);
80 TRACE("%p\n", jsthis);
82 *r = jsval_number(jsstr_length(string->str));
83 return S_OK;
86 static HRESULT stringobj_to_string(jsval_t vthis, jsval_t *r)
88 StringInstance *string;
90 if(!(string = string_this(vthis))) {
91 WARN("this is not a string object\n");
92 return E_FAIL;
95 if(r)
96 *r = jsval_string(jsstr_addref(string->str));
97 return S_OK;
100 /* ECMA-262 3rd Edition 15.5.4.2 */
101 static HRESULT String_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
102 jsval_t *r)
104 TRACE("\n");
106 return stringobj_to_string(vthis, r);
109 /* ECMA-262 3rd Edition 15.5.4.2 */
110 static HRESULT String_valueOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
111 jsval_t *r)
113 TRACE("\n");
115 return stringobj_to_string(vthis, r);
118 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, jsval_t vthis, jsval_t *r, const WCHAR *tagname)
120 unsigned tagname_len;
121 jsstr_t *str, *ret;
122 WCHAR *ptr;
123 HRESULT hres;
125 hres = get_string_val(ctx, vthis, &str);
126 if(FAILED(hres))
127 return hres;
129 if(!r) {
130 jsstr_release(str);
131 return S_OK;
134 tagname_len = lstrlenW(tagname);
136 ret = jsstr_alloc_buf(jsstr_length(str) + 2*tagname_len + 5, &ptr);
137 if(!ret) {
138 jsstr_release(str);
139 return E_OUTOFMEMORY;
142 *ptr++ = '<';
143 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
144 ptr += tagname_len;
145 *ptr++ = '>';
147 ptr += jsstr_flush(str, ptr);
148 jsstr_release(str);
150 *ptr++ = '<';
151 *ptr++ = '/';
152 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
153 ptr += tagname_len;
154 *ptr = '>';
156 *r = jsval_string(ret);
157 return S_OK;
160 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, jsval_t vthis, unsigned argc, jsval_t *argv, jsval_t *r,
161 const WCHAR *tagname, const WCHAR *attrname)
163 jsstr_t *str, *attr_value = NULL;
164 HRESULT hres;
166 hres = get_string_val(ctx, vthis, &str);
167 if(FAILED(hres))
168 return hres;
170 if(argc) {
171 hres = to_string(ctx, argv[0], &attr_value);
172 if(FAILED(hres)) {
173 jsstr_release(str);
174 return hres;
176 }else {
177 attr_value = jsstr_undefined();
180 if(r) {
181 unsigned attrname_len = lstrlenW(attrname);
182 unsigned tagname_len = lstrlenW(tagname);
183 jsstr_t *ret;
184 WCHAR *ptr;
186 ret = jsstr_alloc_buf(2*tagname_len + attrname_len + jsstr_length(attr_value) + jsstr_length(str) + 9, &ptr);
187 if(ret) {
188 *ptr++ = '<';
189 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
190 ptr += tagname_len;
191 *ptr++ = ' ';
192 memcpy(ptr, attrname, attrname_len*sizeof(WCHAR));
193 ptr += attrname_len;
194 *ptr++ = '=';
195 *ptr++ = '"';
196 ptr += jsstr_flush(attr_value, ptr);
197 *ptr++ = '"';
198 *ptr++ = '>';
199 ptr += jsstr_flush(str, ptr);
201 *ptr++ = '<';
202 *ptr++ = '/';
203 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
204 ptr += tagname_len;
205 *ptr = '>';
207 *r = jsval_string(ret);
208 }else {
209 hres = E_OUTOFMEMORY;
213 jsstr_release(attr_value);
214 jsstr_release(str);
215 return hres;
218 static HRESULT String_anchor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
219 jsval_t *r)
221 return do_attribute_tag_format(ctx, vthis, argc, argv, r, L"A", L"NAME");
224 static HRESULT String_big(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
225 jsval_t *r)
227 return do_attributeless_tag_format(ctx, vthis, r, L"BIG");
230 static HRESULT String_blink(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
231 jsval_t *r)
233 return do_attributeless_tag_format(ctx, vthis, r, L"BLINK");
236 static HRESULT String_bold(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
237 jsval_t *r)
239 return do_attributeless_tag_format(ctx, vthis, r, L"B");
242 /* ECMA-262 3rd Edition 15.5.4.5 */
243 static HRESULT String_charAt(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
244 jsval_t *r)
246 jsstr_t *str, *ret;
247 INT pos = 0;
248 HRESULT hres;
250 TRACE("\n");
252 hres = get_string_val(ctx, vthis, &str);
253 if(FAILED(hres))
254 return hres;
256 if(argc) {
257 double d;
259 hres = to_integer(ctx, argv[0], &d);
260 if(FAILED(hres)) {
261 jsstr_release(str);
262 return hres;
264 pos = is_int32(d) ? d : -1;
267 if(!r) {
268 jsstr_release(str);
269 return S_OK;
272 if(0 <= pos && pos < jsstr_length(str)) {
273 ret = jsstr_substr(str, pos, 1);
274 if(!ret)
275 return E_OUTOFMEMORY;
276 }else {
277 ret = jsstr_empty();
280 *r = jsval_string(ret);
281 return S_OK;
284 /* ECMA-262 3rd Edition 15.5.4.5 */
285 static HRESULT String_charCodeAt(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
286 jsval_t *r)
288 jsstr_t *str;
289 DWORD idx = 0;
290 HRESULT hres;
292 TRACE("\n");
294 hres = get_string_val(ctx, vthis, &str);
295 if(FAILED(hres))
296 return hres;
298 if(argc > 0) {
299 double d;
301 hres = to_integer(ctx, argv[0], &d);
302 if(FAILED(hres)) {
303 jsstr_release(str);
304 return hres;
307 if(!is_int32(d) || d < 0 || d >= jsstr_length(str)) {
308 jsstr_release(str);
309 if(r)
310 *r = jsval_number(NAN);
311 return S_OK;
314 idx = d;
317 if(r) {
318 WCHAR c;
319 jsstr_extract(str, idx, 1, &c);
320 *r = jsval_number(c);
323 jsstr_release(str);
324 return S_OK;
327 /* ECMA-262 3rd Edition 15.5.4.6 */
328 static HRESULT String_concat(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
329 jsval_t *r)
331 jsstr_t *ret = NULL, *str;
332 HRESULT hres;
334 TRACE("\n");
336 hres = get_string_val(ctx, vthis, &str);
337 if(FAILED(hres))
338 return hres;
340 switch(argc) {
341 case 0:
342 ret = str;
343 break;
344 case 1: {
345 jsstr_t *arg_str;
347 hres = to_string(ctx, argv[0], &arg_str);
348 if(FAILED(hres)) {
349 jsstr_release(str);
350 return hres;
353 ret = jsstr_concat(str, arg_str);
354 jsstr_release(str);
355 if(!ret)
356 return E_OUTOFMEMORY;
357 break;
359 default: {
360 const unsigned str_cnt = argc+1;
361 unsigned len = 0, i;
362 jsstr_t **strs;
363 WCHAR *ptr;
365 strs = heap_alloc_zero(str_cnt * sizeof(*strs));
366 if(!strs) {
367 jsstr_release(str);
368 return E_OUTOFMEMORY;
371 strs[0] = str;
372 for(i=0; i < argc; i++) {
373 hres = to_string(ctx, argv[i], strs+i+1);
374 if(FAILED(hres))
375 break;
378 if(SUCCEEDED(hres)) {
379 for(i=0; i < str_cnt; i++) {
380 len += jsstr_length(strs[i]);
381 if(len > JSSTR_MAX_LENGTH) {
382 hres = E_OUTOFMEMORY;
383 break;
387 if(SUCCEEDED(hres)) {
388 ret = jsstr_alloc_buf(len, &ptr);
389 if(ret) {
390 for(i=0; i < str_cnt; i++)
391 ptr += jsstr_flush(strs[i], ptr);
392 }else {
393 hres = E_OUTOFMEMORY;
398 while(i--)
399 jsstr_release(strs[i]);
400 heap_free(strs);
401 if(FAILED(hres))
402 return hres;
406 if(r)
407 *r = jsval_string(ret);
408 else
409 jsstr_release(ret);
410 return S_OK;
413 static HRESULT String_fixed(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
414 jsval_t *r)
416 return do_attributeless_tag_format(ctx, vthis, r, L"TT");
419 static HRESULT String_fontcolor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
420 jsval_t *r)
422 return do_attribute_tag_format(ctx, vthis, argc, argv, r, L"FONT", L"COLOR");
425 static HRESULT String_fontsize(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
426 jsval_t *r)
428 return do_attribute_tag_format(ctx, vthis, argc, argv, r, L"FONT", L"SIZE");
431 static HRESULT String_indexOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
432 jsval_t *r)
434 unsigned pos = 0, search_len, length;
435 jsstr_t *search_jsstr, *jsstr;
436 const WCHAR *search_str, *str;
437 INT ret = -1;
438 HRESULT hres;
440 TRACE("\n");
442 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
443 if(FAILED(hres))
444 return hres;
446 if(!argc) {
447 if(r)
448 *r = jsval_number(-1);
449 jsstr_release(jsstr);
450 return S_OK;
453 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
454 if(FAILED(hres)) {
455 jsstr_release(jsstr);
456 return hres;
459 search_len = jsstr_length(search_jsstr);
460 length = jsstr_length(jsstr);
462 if(argc >= 2) {
463 double d;
465 hres = to_integer(ctx, argv[1], &d);
466 if(SUCCEEDED(hres) && d > 0.0)
467 pos = is_int32(d) ? min(length, d) : length;
470 if(SUCCEEDED(hres) && length >= search_len) {
471 const WCHAR *end = str+length-search_len;
472 const WCHAR *ptr;
474 for(ptr = str+pos; ptr <= end; ptr++) {
475 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
476 ret = ptr-str;
477 break;
482 jsstr_release(search_jsstr);
483 jsstr_release(jsstr);
484 if(FAILED(hres))
485 return hres;
487 if(r)
488 *r = jsval_number(ret);
489 return S_OK;
492 static HRESULT String_italics(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
493 jsval_t *r)
495 return do_attributeless_tag_format(ctx, vthis, r, L"I");
498 /* ECMA-262 3rd Edition 15.5.4.8 */
499 static HRESULT String_lastIndexOf(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
500 jsval_t *r)
502 unsigned pos = 0, search_len, length;
503 jsstr_t *search_jsstr, *jsstr;
504 const WCHAR *search_str, *str;
505 INT ret = -1;
506 HRESULT hres;
508 TRACE("\n");
510 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
511 if(FAILED(hres))
512 return hres;
514 if(!argc) {
515 if(r)
516 *r = jsval_number(-1);
517 jsstr_release(jsstr);
518 return S_OK;
521 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
522 if(FAILED(hres)) {
523 jsstr_release(jsstr);
524 return hres;
527 search_len = jsstr_length(search_jsstr);
528 length = jsstr_length(jsstr);
530 if(argc >= 2) {
531 double d;
533 hres = to_integer(ctx, argv[1], &d);
534 if(SUCCEEDED(hres) && d > 0)
535 pos = is_int32(d) ? min(length, d) : length;
536 }else {
537 pos = length;
540 if(SUCCEEDED(hres) && length >= search_len) {
541 const WCHAR *ptr;
543 for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) {
544 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
545 ret = ptr-str;
546 break;
551 jsstr_release(search_jsstr);
552 jsstr_release(jsstr);
553 if(FAILED(hres))
554 return hres;
556 if(r)
557 *r = jsval_number(ret);
558 return S_OK;
561 static HRESULT String_link(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
562 jsval_t *r)
564 return do_attribute_tag_format(ctx, vthis, argc, argv, r, L"A", L"HREF");
567 /* ECMA-262 3rd Edition 15.5.4.10 */
568 static HRESULT String_match(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
569 jsval_t *r)
571 jsdisp_t *regexp = NULL;
572 jsstr_t *str;
573 HRESULT hres;
575 TRACE("\n");
577 if(!argc) {
578 if(r)
579 *r = jsval_null();
580 return S_OK;
583 if(is_object_instance(argv[0])) {
584 regexp = iface_to_jsdisp(get_object(argv[0]));
585 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
586 jsdisp_release(regexp);
587 regexp = NULL;
591 if(!regexp) {
592 jsstr_t *match_str;
594 hres = to_string(ctx, argv[0], &match_str);
595 if(FAILED(hres))
596 return hres;
598 hres = create_regexp(ctx, match_str, 0, &regexp);
599 jsstr_release(match_str);
600 if(FAILED(hres))
601 return hres;
604 hres = get_string_val(ctx, vthis, &str);
605 if(SUCCEEDED(hres))
606 hres = regexp_string_match(ctx, regexp, str, r);
608 jsdisp_release(regexp);
609 jsstr_release(str);
610 return hres;
613 typedef struct {
614 WCHAR *buf;
615 DWORD size;
616 DWORD len;
617 } strbuf_t;
619 static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len)
621 WCHAR *new_buf;
622 DWORD new_size;
624 if(len <= buf->size)
625 return TRUE;
627 new_size = buf->size ? buf->size<<1 : 16;
628 if(new_size < len)
629 new_size = len;
630 if(buf->buf)
631 new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
632 else
633 new_buf = heap_alloc(new_size*sizeof(WCHAR));
634 if(!new_buf)
635 return FALSE;
637 buf->buf = new_buf;
638 buf->size = new_size;
639 return TRUE;
642 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
644 if(!len)
645 return S_OK;
647 if(!strbuf_ensure_size(buf, buf->len+len))
648 return E_OUTOFMEMORY;
650 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
651 buf->len += len;
652 return S_OK;
655 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str)
657 if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str)))
658 return E_OUTOFMEMORY;
660 jsstr_flush(str, buf->buf+buf->len);
661 buf->len += jsstr_length(str);
662 return S_OK;
665 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
666 jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret)
668 jsval_t *argv;
669 unsigned argc;
670 jsval_t val;
671 jsstr_t *tmp_str;
672 DWORD i;
673 HRESULT hres = S_OK;
675 argc = match->paren_count+3;
676 argv = heap_alloc_zero(sizeof(*argv)*argc);
677 if(!argv)
678 return E_OUTOFMEMORY;
680 tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
681 if(!tmp_str)
682 hres = E_OUTOFMEMORY;
683 argv[0] = jsval_string(tmp_str);
685 if(SUCCEEDED(hres)) {
686 for(i=0; i < match->paren_count; i++) {
687 if(match->parens[i].index != -1)
688 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length);
689 else
690 tmp_str = jsstr_empty();
691 if(!tmp_str) {
692 hres = E_OUTOFMEMORY;
693 break;
695 argv[i+1] = jsval_string(tmp_str);
699 if(SUCCEEDED(hres)) {
700 argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len);
701 argv[match->paren_count+2] = jsval_string(jsstr);
704 if(SUCCEEDED(hres))
705 hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val);
707 for(i=0; i <= match->paren_count; i++)
708 jsstr_release(get_string(argv[i]));
709 heap_free(argv);
711 if(FAILED(hres))
712 return hres;
714 hres = to_string(ctx, val, ret);
715 jsval_release(val);
716 return hres;
719 /* ECMA-262 3rd Edition 15.5.4.11 */
720 static HRESULT String_replace(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
721 jsval_t *r)
723 const WCHAR *str, *match_str = NULL, *rep_str = NULL;
724 jsstr_t *rep_jsstr, *match_jsstr, *jsstr;
725 jsdisp_t *rep_func = NULL, *regexp = NULL;
726 match_state_t *match = NULL, last_match = {0};
727 strbuf_t ret = {NULL,0,0};
728 DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
729 DWORD rep_len=0;
730 HRESULT hres = S_OK;
732 TRACE("\n");
734 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
735 if(FAILED(hres))
736 return hres;
738 if(!argc) {
739 if(r)
740 *r = jsval_string(jsstr);
741 else
742 jsstr_release(jsstr);
743 return S_OK;
746 if(is_object_instance(argv[0])) {
747 regexp = iface_to_jsdisp(get_object(argv[0]));
748 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
749 jsdisp_release(regexp);
750 regexp = NULL;
754 if(!regexp) {
755 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
756 if(FAILED(hres)) {
757 jsstr_release(jsstr);
758 return hres;
762 if(argc >= 2) {
763 if(is_object_instance(argv[1])) {
764 rep_func = iface_to_jsdisp(get_object(argv[1]));
765 if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
766 jsdisp_release(rep_func);
767 rep_func = NULL;
771 if(!rep_func) {
772 hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str);
773 if(SUCCEEDED(hres))
774 rep_len = jsstr_length(rep_jsstr);
778 if(SUCCEEDED(hres)) {
779 const WCHAR *ecp = str;
781 while(1) {
782 if(regexp) {
783 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match);
784 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
786 if(hres == S_FALSE) {
787 hres = S_OK;
788 break;
790 if(FAILED(hres))
791 break;
793 last_match.cp = match->cp;
794 last_match.match_len = match->match_len;
795 }else {
796 if(re_flags & REM_ALLOC_RESULT) {
797 re_flags &= ~REM_ALLOC_RESULT;
798 match = &last_match;
799 match->cp = str;
802 match->cp = wcsstr(match->cp, match_str);
803 if(!match->cp)
804 break;
805 match->match_len = jsstr_length(match_jsstr);
806 match->cp += match->match_len;
809 hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
810 ecp = match->cp;
811 if(FAILED(hres))
812 break;
814 if(rep_func) {
815 jsstr_t *cstr;
817 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr);
818 if(FAILED(hres))
819 break;
821 hres = strbuf_append_jsstr(&ret, cstr);
822 jsstr_release(cstr);
823 if(FAILED(hres))
824 break;
825 }else if(rep_str && regexp) {
826 const WCHAR *ptr = rep_str, *ptr2;
828 while((ptr2 = wcschr(ptr, '$'))) {
829 hres = strbuf_append(&ret, ptr, ptr2-ptr);
830 if(FAILED(hres))
831 break;
833 switch(ptr2[1]) {
834 case '$':
835 hres = strbuf_append(&ret, ptr2, 1);
836 ptr = ptr2+2;
837 break;
838 case '&':
839 hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
840 ptr = ptr2+2;
841 break;
842 case '`':
843 hres = strbuf_append(&ret, str, match->cp-str-match->match_len);
844 ptr = ptr2+2;
845 break;
846 case '\'':
847 hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp);
848 ptr = ptr2+2;
849 break;
850 default: {
851 DWORD idx;
853 if(!is_digit(ptr2[1])) {
854 hres = strbuf_append(&ret, ptr2, 1);
855 ptr = ptr2+1;
856 break;
859 idx = ptr2[1] - '0';
860 if(is_digit(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
861 idx = idx*10 + (ptr[2]-'0');
862 ptr = ptr2+3;
863 }else if(idx && idx <= match->paren_count) {
864 ptr = ptr2+2;
865 }else {
866 hres = strbuf_append(&ret, ptr2, 1);
867 ptr = ptr2+1;
868 break;
871 if(match->parens[idx-1].index != -1)
872 hres = strbuf_append(&ret, str+match->parens[idx-1].index,
873 match->parens[idx-1].length);
877 if(FAILED(hres))
878 break;
881 if(SUCCEEDED(hres))
882 hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
883 if(FAILED(hres))
884 break;
885 }else if(rep_str) {
886 hres = strbuf_append(&ret, rep_str, rep_len);
887 if(FAILED(hres))
888 break;
889 }else {
890 hres = strbuf_append(&ret, L"undefined", ARRAY_SIZE(L"undefined")-1);
891 if(FAILED(hres))
892 break;
895 if(!regexp)
896 break;
897 else if(!match->match_len)
898 match->cp++;
901 if(SUCCEEDED(hres))
902 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
905 if(rep_func)
906 jsdisp_release(rep_func);
907 if(rep_str)
908 jsstr_release(rep_jsstr);
909 if(match_str)
910 jsstr_release(match_jsstr);
911 if(regexp)
912 heap_free(match);
914 if(SUCCEEDED(hres) && last_match.cp && regexp) {
915 jsstr_release(ctx->last_match);
916 ctx->last_match = jsstr_addref(jsstr);
917 ctx->last_match_index = last_match.cp-str-last_match.match_len;
918 ctx->last_match_length = last_match.match_len;
921 if(regexp)
922 jsdisp_release(regexp);
923 jsstr_release(jsstr);
925 if(SUCCEEDED(hres) && r) {
926 jsstr_t *ret_str;
928 ret_str = jsstr_alloc_len(ret.buf, ret.len);
929 if(!ret_str)
930 return E_OUTOFMEMORY;
932 TRACE("= %s\n", debugstr_jsstr(ret_str));
933 *r = jsval_string(ret_str);
936 heap_free(ret.buf);
937 return hres;
940 static HRESULT String_search(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
941 jsval_t *r)
943 jsdisp_t *regexp = NULL;
944 const WCHAR *str;
945 jsstr_t *jsstr;
946 match_state_t match, *match_ptr = &match;
947 HRESULT hres;
949 TRACE("\n");
951 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
952 if(FAILED(hres))
953 return hres;
955 if(!argc) {
956 if(r)
957 *r = jsval_null();
958 jsstr_release(jsstr);
959 return S_OK;
962 if(is_object_instance(argv[0])) {
963 regexp = iface_to_jsdisp(get_object(argv[0]));
964 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
965 jsdisp_release(regexp);
966 regexp = NULL;
970 if(!regexp) {
971 hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
972 if(FAILED(hres)) {
973 jsstr_release(jsstr);
974 return hres;
978 match.cp = str;
979 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
980 jsstr_release(jsstr);
981 jsdisp_release(regexp);
982 if(FAILED(hres))
983 return hres;
985 if(r)
986 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
987 return S_OK;
990 /* ECMA-262 3rd Edition 15.5.4.13 */
991 static HRESULT String_slice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
992 jsval_t *r)
994 int start=0, end, length;
995 jsstr_t *str;
996 double d;
997 HRESULT hres;
999 TRACE("\n");
1001 hres = get_string_val(ctx, vthis, &str);
1002 if(FAILED(hres))
1003 return hres;
1005 length = jsstr_length(str);
1006 if(argc) {
1007 hres = to_integer(ctx, argv[0], &d);
1008 if(FAILED(hres)) {
1009 jsstr_release(str);
1010 return hres;
1013 if(is_int32(d)) {
1014 start = d;
1015 if(start < 0) {
1016 start = length + start;
1017 if(start < 0)
1018 start = 0;
1019 }else if(start > length) {
1020 start = length;
1022 }else if(d > 0) {
1023 start = length;
1027 if(argc >= 2) {
1028 hres = to_integer(ctx, argv[1], &d);
1029 if(FAILED(hres)) {
1030 jsstr_release(str);
1031 return hres;
1034 if(is_int32(d)) {
1035 end = d;
1036 if(end < 0) {
1037 end = length + end;
1038 if(end < 0)
1039 end = 0;
1040 }else if(end > length) {
1041 end = length;
1043 }else {
1044 end = d < 0.0 ? 0 : length;
1046 }else {
1047 end = length;
1050 if(end < start)
1051 end = start;
1053 if(r) {
1054 jsstr_t *retstr = jsstr_substr(str, start, end-start);
1055 if(!retstr) {
1056 jsstr_release(str);
1057 return E_OUTOFMEMORY;
1060 *r = jsval_string(retstr);
1063 jsstr_release(str);
1064 return S_OK;
1067 static HRESULT String_small(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1068 jsval_t *r)
1070 return do_attributeless_tag_format(ctx, vthis, r, L"SMALL");
1073 static HRESULT String_split(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1074 jsval_t *r)
1076 match_state_t match_result, *match_ptr = &match_result;
1077 size_t length, i = 0, match_len = 0;
1078 const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1079 unsigned limit = ~0u;
1080 jsdisp_t *array, *regexp = NULL;
1081 jsstr_t *jsstr, *match_jsstr, *tmp_str;
1082 HRESULT hres;
1084 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
1085 if(FAILED(hres))
1086 return hres;
1087 length = jsstr_length(jsstr);
1089 TRACE("%s\n", debugstr_wn(str, length));
1091 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) {
1092 if(!r)
1093 return S_OK;
1095 hres = create_array(ctx, 0, &array);
1096 if(FAILED(hres))
1097 return hres;
1099 /* NOTE: according to spec, we should respect limit argument here (if provided).
1100 * We have a test showing that it's broken in native IE. */
1101 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr));
1102 if(FAILED(hres)) {
1103 jsdisp_release(array);
1104 return hres;
1107 *r = jsval_obj(array);
1108 return S_OK;
1111 if(argc > 1 && !is_undefined(argv[1])) {
1112 hres = to_uint32(ctx, argv[1], &limit);
1113 if(FAILED(hres)) {
1114 jsstr_release(jsstr);
1115 return hres;
1119 if(is_object_instance(argv[0])) {
1120 regexp = iface_to_jsdisp(get_object(argv[0]));
1121 if(regexp) {
1122 if(!is_class(regexp, JSCLASS_REGEXP)) {
1123 jsdisp_release(regexp);
1124 regexp = NULL;
1129 if(!regexp) {
1130 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1131 if(FAILED(hres)) {
1132 jsstr_release(jsstr);
1133 return hres;
1136 match_len = jsstr_length(match_jsstr);
1137 if(!match_len) {
1138 jsstr_release(match_jsstr);
1139 match_str = NULL;
1143 hres = create_array(ctx, 0, &array);
1145 if(SUCCEEDED(hres)) {
1146 ptr = str;
1147 match_result.cp = str;
1148 while(i < limit) {
1149 if(regexp) {
1150 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1151 if(hres != S_OK)
1152 break;
1153 TRACE("got match %d %ld\n", (int)(match_result.cp - match_result.match_len - str), match_result.match_len);
1154 if(!match_result.match_len) {
1155 /* If an empty string is matched, prevent including any match in the result */
1156 if(!length) {
1157 limit = 0;
1158 break;
1160 if(match_result.cp == ptr) {
1161 match_result.cp++;
1162 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1163 if(hres != S_OK)
1164 break;
1165 TRACE("retried, got match %d %ld\n", (int)(match_result.cp - match_result.match_len - str),
1166 match_result.match_len);
1168 if(!match_result.match_len && match_result.cp == str + length)
1169 break;
1171 ptr2 = match_result.cp - match_result.match_len;
1172 }else if(match_str) {
1173 ptr2 = wcsstr(ptr, match_str);
1174 if(!ptr2)
1175 break;
1176 }else {
1177 if(!*ptr)
1178 break;
1179 ptr2 = ptr+1;
1182 if(!regexp || ptr2 > ptr || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1183 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1184 if(!tmp_str) {
1185 hres = E_OUTOFMEMORY;
1186 break;
1189 hres = jsdisp_propput_idx(array, i++, jsval_string(tmp_str));
1190 jsstr_release(tmp_str);
1191 if(FAILED(hres))
1192 break;
1195 if(regexp)
1196 ptr = match_result.cp;
1197 else if(match_str)
1198 ptr = ptr2 + match_len;
1199 else
1200 ptr++;
1204 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1205 DWORD len = (str+length) - ptr;
1207 if(len || match_str || !length || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1208 tmp_str = jsstr_alloc_len(ptr, len);
1210 if(tmp_str) {
1211 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1212 jsstr_release(tmp_str);
1213 }else {
1214 hres = E_OUTOFMEMORY;
1219 if(regexp)
1220 jsdisp_release(regexp);
1221 if(match_str)
1222 jsstr_release(match_jsstr);
1223 jsstr_release(jsstr);
1225 if(SUCCEEDED(hres) && r)
1226 *r = jsval_obj(array);
1227 else
1228 jsdisp_release(array);
1230 return hres;
1233 static HRESULT String_strike(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1234 jsval_t *r)
1236 return do_attributeless_tag_format(ctx, vthis, r, L"STRIKE");
1239 static HRESULT String_sub(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1240 jsval_t *r)
1242 return do_attributeless_tag_format(ctx, vthis, r, L"SUB");
1245 /* ECMA-262 3rd Edition 15.5.4.15 */
1246 static HRESULT String_substring(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1247 jsval_t *r)
1249 INT start=0, end, length;
1250 jsstr_t *str;
1251 double d;
1252 HRESULT hres;
1254 TRACE("\n");
1256 hres = get_string_val(ctx, vthis, &str);
1257 if(FAILED(hres))
1258 return hres;
1260 length = jsstr_length(str);
1261 if(argc >= 1) {
1262 hres = to_integer(ctx, argv[0], &d);
1263 if(FAILED(hres)) {
1264 jsstr_release(str);
1265 return hres;
1268 if(d >= 0)
1269 start = is_int32(d) ? min(length, d) : length;
1272 if(argc >= 2) {
1273 hres = to_integer(ctx, argv[1], &d);
1274 if(FAILED(hres)) {
1275 jsstr_release(str);
1276 return hres;
1279 if(d >= 0)
1280 end = is_int32(d) ? min(length, d) : length;
1281 else
1282 end = 0;
1283 }else {
1284 end = length;
1287 if(start > end) {
1288 INT tmp = start;
1289 start = end;
1290 end = tmp;
1293 if(r) {
1294 jsstr_t *ret = jsstr_substr(str, start, end-start);
1295 if(ret)
1296 *r = jsval_string(ret);
1297 else
1298 hres = E_OUTOFMEMORY;
1300 jsstr_release(str);
1301 return hres;
1304 /* ECMA-262 3rd Edition B.2.3 */
1305 static HRESULT String_substr(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1306 jsval_t *r)
1308 int start=0, len, length;
1309 jsstr_t *str;
1310 double d;
1311 HRESULT hres;
1313 TRACE("\n");
1315 hres = get_string_val(ctx, vthis, &str);
1316 if(FAILED(hres))
1317 return hres;
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;
1327 if(d >= 0)
1328 start = is_int32(d) ? min(length, d) : length;
1331 if(argc >= 2) {
1332 hres = to_integer(ctx, argv[1], &d);
1333 if(FAILED(hres)) {
1334 jsstr_release(str);
1335 return hres;
1338 if(d >= 0.0)
1339 len = is_int32(d) ? min(length-start, d) : length-start;
1340 else
1341 len = 0;
1342 }else {
1343 len = length-start;
1346 hres = S_OK;
1347 if(r) {
1348 jsstr_t *ret = jsstr_substr(str, start, len);
1349 if(ret)
1350 *r = jsval_string(ret);
1351 else
1352 hres = E_OUTOFMEMORY;
1355 jsstr_release(str);
1356 return hres;
1359 static HRESULT String_sup(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1360 jsval_t *r)
1362 return do_attributeless_tag_format(ctx, vthis, r, L"SUP");
1365 static HRESULT to_upper_case(script_ctx_t *ctx, jsval_t vthis, jsval_t *r)
1367 jsstr_t *str;
1368 HRESULT hres;
1371 hres = get_string_val(ctx, vthis, &str);
1372 if(FAILED(hres))
1373 return hres;
1375 if(r) {
1376 unsigned len = jsstr_length(str);
1377 jsstr_t *ret;
1378 WCHAR *buf;
1380 ret = jsstr_alloc_buf(len, &buf);
1381 if(!ret) {
1382 jsstr_release(str);
1383 return E_OUTOFMEMORY;
1386 jsstr_flush(str, buf);
1387 for (; len--; buf++) *buf = towupper(*buf);
1389 *r = jsval_string(ret);
1391 jsstr_release(str);
1392 return S_OK;
1395 static HRESULT to_lower_case(script_ctx_t *ctx, jsval_t vthis, jsval_t *r)
1397 jsstr_t *str;
1398 HRESULT hres;
1401 hres = get_string_val(ctx, vthis, &str);
1402 if(FAILED(hres))
1403 return hres;
1405 if(r) {
1406 unsigned len = jsstr_length(str);
1407 jsstr_t *ret;
1408 WCHAR *buf;
1410 ret = jsstr_alloc_buf(len, &buf);
1411 if(!ret) {
1412 jsstr_release(str);
1413 return E_OUTOFMEMORY;
1416 jsstr_flush(str, buf);
1417 for (; len--; buf++) *buf = towlower(*buf);
1419 *r = jsval_string(ret);
1421 jsstr_release(str);
1422 return S_OK;
1425 static HRESULT String_toLowerCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1426 jsval_t *r)
1428 TRACE("\n");
1429 return to_lower_case(ctx, vthis, r);
1432 static HRESULT String_toUpperCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1433 jsval_t *r)
1435 TRACE("\n");
1436 return to_upper_case(ctx, vthis, r);
1439 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1440 jsval_t *r)
1442 TRACE("\n");
1443 return to_lower_case(ctx, vthis, r);
1446 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1447 jsval_t *r)
1449 TRACE("\n");
1450 return to_upper_case(ctx, vthis, r);
1453 static HRESULT String_trim(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc,
1454 jsval_t *argv, jsval_t *r)
1456 const WCHAR *str, *begin, *end;
1457 jsstr_t *jsstr;
1458 unsigned len;
1459 HRESULT hres;
1461 if(is_undefined(vthis) || is_null(vthis))
1462 return JS_E_OBJECT_EXPECTED;
1464 hres = to_flat_string(ctx, vthis, &jsstr, &str);
1465 if(FAILED(hres)) {
1466 WARN("to_flat_string failed: %08lx\n", hres);
1467 return hres;
1469 len = jsstr_length(jsstr);
1470 TRACE("%s\n", debugstr_wn(str, len));
1472 for(begin = str, end = str + len; begin < end && iswspace(*begin); begin++);
1473 while(end > begin + 1 && iswspace(*(end-1))) end--;
1475 if(r) {
1476 jsstr_t *ret;
1478 if(begin == str && end == str + len)
1479 ret = jsstr_addref(jsstr);
1480 else
1481 ret = jsstr_alloc_len(begin, end - begin);
1482 if(ret)
1483 *r = jsval_string(ret);
1484 else
1485 hres = E_OUTOFMEMORY;
1487 jsstr_release(jsstr);
1488 return hres;
1491 static HRESULT String_localeCompare(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1492 jsval_t *r)
1494 FIXME("\n");
1495 return E_NOTIMPL;
1498 static void String_destructor(jsdisp_t *dispex)
1500 StringInstance *This = string_from_jsdisp(dispex);
1502 jsstr_release(This->str);
1503 heap_free(This);
1506 static unsigned String_idx_length(jsdisp_t *jsdisp)
1508 StringInstance *string = string_from_jsdisp(jsdisp);
1511 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1512 * Newer jscript.dll versions implement it on string type, not class,
1513 * which is not how it should work according to spec. IE9 implements it
1514 * properly, but it uses its own JavaScript engine inside MSHTML. We
1515 * implement it here, but in the way IE9 and spec work.
1517 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1520 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1522 StringInstance *string = string_from_jsdisp(jsdisp);
1523 jsstr_t *ret;
1525 ret = jsstr_substr(string->str, idx, 1);
1526 if(!ret)
1527 return E_OUTOFMEMORY;
1529 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1531 *r = jsval_string(ret);
1532 return S_OK;
1535 static const builtin_prop_t String_props[] = {
1536 {L"anchor", String_anchor, PROPF_METHOD|1},
1537 {L"big", String_big, PROPF_METHOD},
1538 {L"blink", String_blink, PROPF_METHOD},
1539 {L"bold", String_bold, PROPF_METHOD},
1540 {L"charAt", String_charAt, PROPF_METHOD|1},
1541 {L"charCodeAt", String_charCodeAt, PROPF_METHOD|1},
1542 {L"concat", String_concat, PROPF_METHOD|1},
1543 {L"fixed", String_fixed, PROPF_METHOD},
1544 {L"fontcolor", String_fontcolor, PROPF_METHOD|1},
1545 {L"fontsize", String_fontsize, PROPF_METHOD|1},
1546 {L"indexOf", String_indexOf, PROPF_METHOD|2},
1547 {L"italics", String_italics, PROPF_METHOD},
1548 {L"lastIndexOf", String_lastIndexOf, PROPF_METHOD|2},
1549 {L"length", NULL,0, String_get_length},
1550 {L"link", String_link, PROPF_METHOD|1},
1551 {L"localeCompare", String_localeCompare, PROPF_METHOD|1},
1552 {L"match", String_match, PROPF_METHOD|1},
1553 {L"replace", String_replace, PROPF_METHOD|1},
1554 {L"search", String_search, PROPF_METHOD},
1555 {L"slice", String_slice, PROPF_METHOD},
1556 {L"small", String_small, PROPF_METHOD},
1557 {L"split", String_split, PROPF_METHOD|2},
1558 {L"strike", String_strike, PROPF_METHOD},
1559 {L"sub", String_sub, PROPF_METHOD},
1560 {L"substr", String_substr, PROPF_METHOD|2},
1561 {L"substring", String_substring, PROPF_METHOD|2},
1562 {L"sup", String_sup, PROPF_METHOD},
1563 {L"toLocaleLowerCase", String_toLocaleLowerCase, PROPF_METHOD},
1564 {L"toLocaleUpperCase", String_toLocaleUpperCase, PROPF_METHOD},
1565 {L"toLowerCase", String_toLowerCase, PROPF_METHOD},
1566 {L"toString", String_toString, PROPF_METHOD},
1567 {L"toUpperCase", String_toUpperCase, PROPF_METHOD},
1568 {L"trim", String_trim, PROPF_ES5|PROPF_METHOD},
1569 {L"valueOf", String_valueOf, PROPF_METHOD}
1572 static const builtin_info_t String_info = {
1573 JSCLASS_STRING,
1574 NULL,
1575 ARRAY_SIZE(String_props),
1576 String_props,
1577 String_destructor,
1578 NULL
1581 static const builtin_prop_t StringInst_props[] = {
1582 {L"length", NULL,0, String_get_length}
1585 static const builtin_info_t StringInst_info = {
1586 JSCLASS_STRING,
1587 NULL,
1588 ARRAY_SIZE(StringInst_props),
1589 StringInst_props,
1590 String_destructor,
1591 NULL,
1592 String_idx_length,
1593 String_idx_get
1596 /* ECMA-262 3rd Edition 15.5.3.2 */
1597 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, jsval_t vthis, WORD flags,
1598 unsigned argc, jsval_t *argv, jsval_t *r)
1600 WCHAR *ret_str;
1601 UINT32 i, code;
1602 jsstr_t *ret;
1603 HRESULT hres;
1605 TRACE("\n");
1607 ret = jsstr_alloc_buf(argc, &ret_str);
1608 if(!ret)
1609 return E_OUTOFMEMORY;
1611 for(i=0; i<argc; i++) {
1612 hres = to_uint32(ctx, argv[i], &code);
1613 if(FAILED(hres)) {
1614 jsstr_release(ret);
1615 return hres;
1618 ret_str[i] = code;
1621 if(r)
1622 *r = jsval_string(ret);
1623 else
1624 jsstr_release(ret);
1625 return S_OK;
1628 static HRESULT StringConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1629 jsval_t *r)
1631 HRESULT hres = S_OK;
1633 TRACE("\n");
1635 switch(flags) {
1636 case INVOKE_FUNC: {
1637 jsstr_t *str;
1639 if(argc) {
1640 hres = to_string(ctx, argv[0], &str);
1641 if(FAILED(hres))
1642 return hres;
1643 }else {
1644 str = jsstr_empty();
1647 if(r) *r = jsval_string(str);
1648 else jsstr_release(str);
1649 break;
1651 case DISPATCH_CONSTRUCT: {
1652 jsstr_t *str;
1653 jsdisp_t *ret;
1655 if(argc) {
1656 hres = to_string(ctx, argv[0], &str);
1657 if(FAILED(hres))
1658 return hres;
1659 }else {
1660 str = jsstr_empty();
1663 if(r) {
1664 hres = create_string(ctx, str, &ret);
1665 if(SUCCEEDED(hres)) *r = jsval_obj(ret);
1667 jsstr_release(str);
1668 return hres;
1671 default:
1672 FIXME("unimplemented flags: %x\n", flags);
1673 return E_NOTIMPL;
1676 return S_OK;
1679 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1681 StringInstance *string;
1682 HRESULT hres;
1684 string = heap_alloc_zero(sizeof(StringInstance));
1685 if(!string)
1686 return E_OUTOFMEMORY;
1688 if(object_prototype)
1689 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1690 else
1691 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1692 if(FAILED(hres)) {
1693 heap_free(string);
1694 return hres;
1697 string->str = jsstr_addref(str);
1698 *ret = string;
1699 return S_OK;
1702 static const builtin_prop_t StringConstr_props[] = {
1703 {L"fromCharCode", StringConstr_fromCharCode, PROPF_METHOD},
1706 static const builtin_info_t StringConstr_info = {
1707 JSCLASS_FUNCTION,
1708 Function_value,
1709 ARRAY_SIZE(StringConstr_props),
1710 StringConstr_props,
1711 NULL,
1712 NULL
1715 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1717 StringInstance *string;
1718 HRESULT hres;
1720 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1721 if(FAILED(hres))
1722 return hres;
1724 hres = create_builtin_constructor(ctx, StringConstr_value, L"String", &StringConstr_info,
1725 PROPF_CONSTR|1, &string->dispex, ret);
1727 jsdisp_release(&string->dispex);
1728 return hres;
1731 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1733 StringInstance *string;
1734 HRESULT hres;
1736 hres = string_alloc(ctx, NULL, str, &string);
1737 if(FAILED(hres))
1738 return hres;
1740 *ret = &string->dispex;
1741 return S_OK;