opengl32: Preserve the remainder of the version string when limiting the version...
[wine.git] / dlls / jscript / string.c
blob4033f0a2b56686b2fb2aee549c33ef682c857168
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 = calloc(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 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 new_buf = realloc(buf->buf, new_size * sizeof(WCHAR));
631 if(!new_buf)
632 return FALSE;
634 buf->buf = new_buf;
635 buf->size = new_size;
636 return TRUE;
639 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
641 if(!len)
642 return S_OK;
644 if(!strbuf_ensure_size(buf, buf->len+len))
645 return E_OUTOFMEMORY;
647 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
648 buf->len += len;
649 return S_OK;
652 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str)
654 if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str)))
655 return E_OUTOFMEMORY;
657 jsstr_flush(str, buf->buf+buf->len);
658 buf->len += jsstr_length(str);
659 return S_OK;
662 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
663 jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret)
665 jsval_t *argv;
666 unsigned argc;
667 jsval_t val;
668 jsstr_t *tmp_str;
669 DWORD i;
670 HRESULT hres = S_OK;
672 argc = match->paren_count+3;
673 argv = calloc(argc, sizeof(*argv));
674 if(!argv)
675 return E_OUTOFMEMORY;
677 tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
678 if(!tmp_str)
679 hres = E_OUTOFMEMORY;
680 argv[0] = jsval_string(tmp_str);
682 if(SUCCEEDED(hres)) {
683 for(i=0; i < match->paren_count; i++) {
684 if(match->parens[i].index != -1)
685 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length);
686 else
687 tmp_str = jsstr_empty();
688 if(!tmp_str) {
689 hres = E_OUTOFMEMORY;
690 break;
692 argv[i+1] = jsval_string(tmp_str);
696 if(SUCCEEDED(hres)) {
697 argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len);
698 argv[match->paren_count+2] = jsval_string(jsstr);
701 if(SUCCEEDED(hres))
702 hres = jsdisp_call_value(func, jsval_undefined(), DISPATCH_METHOD, argc, argv, &val);
704 for(i=0; i <= match->paren_count; i++)
705 jsstr_release(get_string(argv[i]));
706 free(argv);
708 if(FAILED(hres))
709 return hres;
711 hres = to_string(ctx, val, ret);
712 jsval_release(val);
713 return hres;
716 /* ECMA-262 3rd Edition 15.5.4.11 */
717 static HRESULT String_replace(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
718 jsval_t *r)
720 const WCHAR *str, *match_str = NULL, *rep_str = NULL;
721 jsstr_t *rep_jsstr, *match_jsstr, *jsstr;
722 jsdisp_t *rep_func = NULL, *regexp = NULL;
723 match_state_t *match = NULL, last_match = {0};
724 strbuf_t ret = {NULL,0,0};
725 DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
726 DWORD rep_len=0;
727 HRESULT hres = S_OK;
729 TRACE("\n");
731 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
732 if(FAILED(hres))
733 return hres;
735 if(!argc) {
736 if(r)
737 *r = jsval_string(jsstr);
738 else
739 jsstr_release(jsstr);
740 return S_OK;
743 if(is_object_instance(argv[0])) {
744 regexp = iface_to_jsdisp(get_object(argv[0]));
745 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
746 jsdisp_release(regexp);
747 regexp = NULL;
751 if(!regexp) {
752 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
753 if(FAILED(hres)) {
754 jsstr_release(jsstr);
755 return hres;
759 if(argc >= 2) {
760 if(is_object_instance(argv[1])) {
761 rep_func = iface_to_jsdisp(get_object(argv[1]));
762 if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
763 jsdisp_release(rep_func);
764 rep_func = NULL;
768 if(!rep_func) {
769 hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str);
770 if(SUCCEEDED(hres))
771 rep_len = jsstr_length(rep_jsstr);
775 if(SUCCEEDED(hres)) {
776 const WCHAR *ecp = str;
778 while(1) {
779 if(regexp) {
780 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match);
781 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
783 if(hres == S_FALSE) {
784 hres = S_OK;
785 break;
787 if(FAILED(hres))
788 break;
790 last_match.cp = match->cp;
791 last_match.match_len = match->match_len;
792 }else {
793 if(re_flags & REM_ALLOC_RESULT) {
794 re_flags &= ~REM_ALLOC_RESULT;
795 match = &last_match;
796 match->cp = str;
799 match->cp = wcsstr(match->cp, match_str);
800 if(!match->cp)
801 break;
802 match->match_len = jsstr_length(match_jsstr);
803 match->cp += match->match_len;
806 hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
807 ecp = match->cp;
808 if(FAILED(hres))
809 break;
811 if(rep_func) {
812 jsstr_t *cstr;
814 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr);
815 if(FAILED(hres))
816 break;
818 hres = strbuf_append_jsstr(&ret, cstr);
819 jsstr_release(cstr);
820 if(FAILED(hres))
821 break;
822 }else if(rep_str && regexp) {
823 const WCHAR *ptr = rep_str, *ptr2;
825 while((ptr2 = wcschr(ptr, '$'))) {
826 hres = strbuf_append(&ret, ptr, ptr2-ptr);
827 if(FAILED(hres))
828 break;
830 switch(ptr2[1]) {
831 case '$':
832 hres = strbuf_append(&ret, ptr2, 1);
833 ptr = ptr2+2;
834 break;
835 case '&':
836 hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
837 ptr = ptr2+2;
838 break;
839 case '`':
840 hres = strbuf_append(&ret, str, match->cp-str-match->match_len);
841 ptr = ptr2+2;
842 break;
843 case '\'':
844 hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp);
845 ptr = ptr2+2;
846 break;
847 default: {
848 DWORD idx;
850 if(!is_digit(ptr2[1])) {
851 hres = strbuf_append(&ret, ptr2, 1);
852 ptr = ptr2+1;
853 break;
856 idx = ptr2[1] - '0';
857 if(is_digit(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
858 idx = idx*10 + (ptr[2]-'0');
859 ptr = ptr2+3;
860 }else if(idx && idx <= match->paren_count) {
861 ptr = ptr2+2;
862 }else {
863 hres = strbuf_append(&ret, ptr2, 1);
864 ptr = ptr2+1;
865 break;
868 if(match->parens[idx-1].index != -1)
869 hres = strbuf_append(&ret, str+match->parens[idx-1].index,
870 match->parens[idx-1].length);
874 if(FAILED(hres))
875 break;
878 if(SUCCEEDED(hres))
879 hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
880 if(FAILED(hres))
881 break;
882 }else if(rep_str) {
883 hres = strbuf_append(&ret, rep_str, rep_len);
884 if(FAILED(hres))
885 break;
886 }else {
887 hres = strbuf_append(&ret, L"undefined", ARRAY_SIZE(L"undefined")-1);
888 if(FAILED(hres))
889 break;
892 if(!regexp)
893 break;
894 else if(!match->match_len)
895 match->cp++;
898 if(SUCCEEDED(hres))
899 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
902 if(rep_func)
903 jsdisp_release(rep_func);
904 if(rep_str)
905 jsstr_release(rep_jsstr);
906 if(match_str)
907 jsstr_release(match_jsstr);
908 if(regexp)
909 free(match);
911 if(SUCCEEDED(hres) && last_match.cp && regexp) {
912 jsstr_release(ctx->last_match);
913 ctx->last_match = jsstr_addref(jsstr);
914 ctx->last_match_index = last_match.cp-str-last_match.match_len;
915 ctx->last_match_length = last_match.match_len;
918 if(regexp)
919 jsdisp_release(regexp);
920 jsstr_release(jsstr);
922 if(SUCCEEDED(hres) && r) {
923 jsstr_t *ret_str;
925 ret_str = jsstr_alloc_len(ret.buf, ret.len);
926 if(!ret_str) {
927 free(ret.buf);
928 return E_OUTOFMEMORY;
931 TRACE("= %s\n", debugstr_jsstr(ret_str));
932 *r = jsval_string(ret_str);
935 free(ret.buf);
936 return hres;
939 static HRESULT String_search(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
940 jsval_t *r)
942 jsdisp_t *regexp = NULL;
943 const WCHAR *str;
944 jsstr_t *jsstr;
945 match_state_t match, *match_ptr = &match;
946 HRESULT hres;
948 TRACE("\n");
950 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
951 if(FAILED(hres))
952 return hres;
954 if(!argc) {
955 if(r)
956 *r = jsval_null();
957 jsstr_release(jsstr);
958 return S_OK;
961 if(is_object_instance(argv[0])) {
962 regexp = iface_to_jsdisp(get_object(argv[0]));
963 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
964 jsdisp_release(regexp);
965 regexp = NULL;
969 if(!regexp) {
970 hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
971 if(FAILED(hres)) {
972 jsstr_release(jsstr);
973 return hres;
977 match.cp = str;
978 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
979 jsstr_release(jsstr);
980 jsdisp_release(regexp);
981 if(FAILED(hres))
982 return hres;
984 if(r)
985 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
986 return S_OK;
989 /* ECMA-262 3rd Edition 15.5.4.13 */
990 static HRESULT String_slice(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
991 jsval_t *r)
993 int start=0, end, length;
994 jsstr_t *str;
995 double d;
996 HRESULT hres;
998 TRACE("\n");
1000 hres = get_string_val(ctx, vthis, &str);
1001 if(FAILED(hres))
1002 return hres;
1004 length = jsstr_length(str);
1005 if(argc) {
1006 hres = to_integer(ctx, argv[0], &d);
1007 if(FAILED(hres)) {
1008 jsstr_release(str);
1009 return hres;
1012 if(is_int32(d)) {
1013 start = d;
1014 if(start < 0) {
1015 start = length + start;
1016 if(start < 0)
1017 start = 0;
1018 }else if(start > length) {
1019 start = length;
1021 }else if(d > 0) {
1022 start = length;
1026 if(argc >= 2) {
1027 hres = to_integer(ctx, argv[1], &d);
1028 if(FAILED(hres)) {
1029 jsstr_release(str);
1030 return hres;
1033 if(is_int32(d)) {
1034 end = d;
1035 if(end < 0) {
1036 end = length + end;
1037 if(end < 0)
1038 end = 0;
1039 }else if(end > length) {
1040 end = length;
1042 }else {
1043 end = d < 0.0 ? 0 : length;
1045 }else {
1046 end = length;
1049 if(end < start)
1050 end = start;
1052 if(r) {
1053 jsstr_t *retstr = jsstr_substr(str, start, end-start);
1054 if(!retstr) {
1055 jsstr_release(str);
1056 return E_OUTOFMEMORY;
1059 *r = jsval_string(retstr);
1062 jsstr_release(str);
1063 return S_OK;
1066 static HRESULT String_small(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1067 jsval_t *r)
1069 return do_attributeless_tag_format(ctx, vthis, r, L"SMALL");
1072 static HRESULT String_split(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1073 jsval_t *r)
1075 match_state_t match_result, *match_ptr = &match_result;
1076 size_t length, i = 0, match_len = 0;
1077 const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1078 unsigned limit = ~0u;
1079 jsdisp_t *array, *regexp = NULL;
1080 jsstr_t *jsstr, *match_jsstr, *tmp_str;
1081 HRESULT hres;
1083 hres = get_string_flat_val(ctx, vthis, &jsstr, &str);
1084 if(FAILED(hres))
1085 return hres;
1086 length = jsstr_length(jsstr);
1088 TRACE("%s\n", debugstr_wn(str, length));
1090 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) {
1091 if(!r)
1092 return S_OK;
1094 hres = create_array(ctx, 0, &array);
1095 if(FAILED(hres))
1096 return hres;
1098 /* NOTE: according to spec, we should respect limit argument here (if provided).
1099 * We have a test showing that it's broken in native IE. */
1100 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr));
1101 if(FAILED(hres)) {
1102 jsdisp_release(array);
1103 return hres;
1106 *r = jsval_obj(array);
1107 return S_OK;
1110 if(argc > 1 && !is_undefined(argv[1])) {
1111 hres = to_uint32(ctx, argv[1], &limit);
1112 if(FAILED(hres)) {
1113 jsstr_release(jsstr);
1114 return hres;
1118 if(is_object_instance(argv[0])) {
1119 regexp = iface_to_jsdisp(get_object(argv[0]));
1120 if(regexp) {
1121 if(!is_class(regexp, JSCLASS_REGEXP)) {
1122 jsdisp_release(regexp);
1123 regexp = NULL;
1128 if(!regexp) {
1129 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1130 if(FAILED(hres)) {
1131 jsstr_release(jsstr);
1132 return hres;
1135 match_len = jsstr_length(match_jsstr);
1136 if(!match_len) {
1137 jsstr_release(match_jsstr);
1138 match_str = NULL;
1142 hres = create_array(ctx, 0, &array);
1144 if(SUCCEEDED(hres)) {
1145 ptr = str;
1146 match_result.cp = str;
1147 while(i < limit) {
1148 if(regexp) {
1149 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1150 if(hres != S_OK)
1151 break;
1152 TRACE("got match %d %ld\n", (int)(match_result.cp - match_result.match_len - str), match_result.match_len);
1153 if(!match_result.match_len) {
1154 /* If an empty string is matched, prevent including any match in the result */
1155 if(!length) {
1156 limit = 0;
1157 break;
1159 if(match_result.cp == ptr) {
1160 match_result.cp++;
1161 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1162 if(hres != S_OK)
1163 break;
1164 TRACE("retried, got match %d %ld\n", (int)(match_result.cp - match_result.match_len - str),
1165 match_result.match_len);
1167 if(!match_result.match_len && match_result.cp == str + length)
1168 break;
1170 ptr2 = match_result.cp - match_result.match_len;
1171 }else if(match_str) {
1172 ptr2 = wcsstr(ptr, match_str);
1173 if(!ptr2)
1174 break;
1175 }else {
1176 if(!*ptr)
1177 break;
1178 ptr2 = ptr+1;
1181 if(!regexp || ptr2 > ptr || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1182 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1183 if(!tmp_str) {
1184 hres = E_OUTOFMEMORY;
1185 break;
1188 hres = jsdisp_propput_idx(array, i++, jsval_string(tmp_str));
1189 jsstr_release(tmp_str);
1190 if(FAILED(hres))
1191 break;
1194 if(regexp)
1195 ptr = match_result.cp;
1196 else if(match_str)
1197 ptr = ptr2 + match_len;
1198 else
1199 ptr++;
1203 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1204 DWORD len = (str+length) - ptr;
1206 if(len || match_str || !length || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1207 tmp_str = jsstr_alloc_len(ptr, len);
1209 if(tmp_str) {
1210 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1211 jsstr_release(tmp_str);
1212 }else {
1213 hres = E_OUTOFMEMORY;
1218 if(regexp)
1219 jsdisp_release(regexp);
1220 if(match_str)
1221 jsstr_release(match_jsstr);
1222 jsstr_release(jsstr);
1224 if(SUCCEEDED(hres) && r)
1225 *r = jsval_obj(array);
1226 else
1227 jsdisp_release(array);
1229 return hres;
1232 static HRESULT String_strike(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1233 jsval_t *r)
1235 return do_attributeless_tag_format(ctx, vthis, r, L"STRIKE");
1238 static HRESULT String_sub(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1239 jsval_t *r)
1241 return do_attributeless_tag_format(ctx, vthis, r, L"SUB");
1244 /* ECMA-262 3rd Edition 15.5.4.15 */
1245 static HRESULT String_substring(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1246 jsval_t *r)
1248 INT start=0, end, length;
1249 jsstr_t *str;
1250 double d;
1251 HRESULT hres;
1253 TRACE("\n");
1255 hres = get_string_val(ctx, vthis, &str);
1256 if(FAILED(hres))
1257 return hres;
1259 length = jsstr_length(str);
1260 if(argc >= 1) {
1261 hres = to_integer(ctx, argv[0], &d);
1262 if(FAILED(hres)) {
1263 jsstr_release(str);
1264 return hres;
1267 if(d >= 0)
1268 start = is_int32(d) ? min(length, d) : length;
1271 if(argc >= 2) {
1272 hres = to_integer(ctx, argv[1], &d);
1273 if(FAILED(hres)) {
1274 jsstr_release(str);
1275 return hres;
1278 if(d >= 0)
1279 end = is_int32(d) ? min(length, d) : length;
1280 else
1281 end = 0;
1282 }else {
1283 end = length;
1286 if(start > end) {
1287 INT tmp = start;
1288 start = end;
1289 end = tmp;
1292 if(r) {
1293 jsstr_t *ret = jsstr_substr(str, start, end-start);
1294 if(ret)
1295 *r = jsval_string(ret);
1296 else
1297 hres = E_OUTOFMEMORY;
1299 jsstr_release(str);
1300 return hres;
1303 /* ECMA-262 3rd Edition B.2.3 */
1304 static HRESULT String_substr(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1305 jsval_t *r)
1307 int start=0, len, length;
1308 jsstr_t *str;
1309 double d;
1310 HRESULT hres;
1312 TRACE("\n");
1314 hres = get_string_val(ctx, vthis, &str);
1315 if(FAILED(hres))
1316 return hres;
1318 length = jsstr_length(str);
1319 if(argc >= 1) {
1320 hres = to_integer(ctx, argv[0], &d);
1321 if(FAILED(hres)) {
1322 jsstr_release(str);
1323 return hres;
1326 if(d >= 0)
1327 start = is_int32(d) ? min(length, d) : length;
1330 if(argc >= 2) {
1331 hres = to_integer(ctx, argv[1], &d);
1332 if(FAILED(hres)) {
1333 jsstr_release(str);
1334 return hres;
1337 if(d >= 0.0)
1338 len = is_int32(d) ? min(length-start, d) : length-start;
1339 else
1340 len = 0;
1341 }else {
1342 len = length-start;
1345 hres = S_OK;
1346 if(r) {
1347 jsstr_t *ret = jsstr_substr(str, start, len);
1348 if(ret)
1349 *r = jsval_string(ret);
1350 else
1351 hres = E_OUTOFMEMORY;
1354 jsstr_release(str);
1355 return hres;
1358 static HRESULT String_sup(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1359 jsval_t *r)
1361 return do_attributeless_tag_format(ctx, vthis, r, L"SUP");
1364 static HRESULT to_upper_case(script_ctx_t *ctx, jsval_t vthis, jsval_t *r)
1366 jsstr_t *str;
1367 HRESULT hres;
1370 hres = get_string_val(ctx, vthis, &str);
1371 if(FAILED(hres))
1372 return hres;
1374 if(r) {
1375 unsigned len = jsstr_length(str);
1376 jsstr_t *ret;
1377 WCHAR *buf;
1379 ret = jsstr_alloc_buf(len, &buf);
1380 if(!ret) {
1381 jsstr_release(str);
1382 return E_OUTOFMEMORY;
1385 jsstr_flush(str, buf);
1386 for (; len--; buf++) *buf = towupper(*buf);
1388 *r = jsval_string(ret);
1390 jsstr_release(str);
1391 return S_OK;
1394 static HRESULT to_lower_case(script_ctx_t *ctx, jsval_t vthis, jsval_t *r)
1396 jsstr_t *str;
1397 HRESULT hres;
1400 hres = get_string_val(ctx, vthis, &str);
1401 if(FAILED(hres))
1402 return hres;
1404 if(r) {
1405 unsigned len = jsstr_length(str);
1406 jsstr_t *ret;
1407 WCHAR *buf;
1409 ret = jsstr_alloc_buf(len, &buf);
1410 if(!ret) {
1411 jsstr_release(str);
1412 return E_OUTOFMEMORY;
1415 jsstr_flush(str, buf);
1416 for (; len--; buf++) *buf = towlower(*buf);
1418 *r = jsval_string(ret);
1420 jsstr_release(str);
1421 return S_OK;
1424 static HRESULT String_toLowerCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1425 jsval_t *r)
1427 TRACE("\n");
1428 return to_lower_case(ctx, vthis, r);
1431 static HRESULT String_toUpperCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1432 jsval_t *r)
1434 TRACE("\n");
1435 return to_upper_case(ctx, vthis, r);
1438 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1439 jsval_t *r)
1441 TRACE("\n");
1442 return to_lower_case(ctx, vthis, r);
1445 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1446 jsval_t *r)
1448 TRACE("\n");
1449 return to_upper_case(ctx, vthis, r);
1452 static HRESULT String_trim(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc,
1453 jsval_t *argv, jsval_t *r)
1455 const WCHAR *str, *begin, *end;
1456 jsstr_t *jsstr;
1457 unsigned len;
1458 HRESULT hres;
1460 if(is_undefined(vthis) || is_null(vthis))
1461 return JS_E_OBJECT_EXPECTED;
1463 hres = to_flat_string(ctx, vthis, &jsstr, &str);
1464 if(FAILED(hres)) {
1465 WARN("to_flat_string failed: %08lx\n", hres);
1466 return hres;
1468 len = jsstr_length(jsstr);
1469 TRACE("%s\n", debugstr_wn(str, len));
1471 for(begin = str, end = str + len; begin < end && iswspace(*begin); begin++);
1472 while(end > begin + 1 && iswspace(*(end-1))) end--;
1474 if(r) {
1475 jsstr_t *ret;
1477 if(begin == str && end == str + len)
1478 ret = jsstr_addref(jsstr);
1479 else
1480 ret = jsstr_alloc_len(begin, end - begin);
1481 if(ret)
1482 *r = jsval_string(ret);
1483 else
1484 hres = E_OUTOFMEMORY;
1486 jsstr_release(jsstr);
1487 return hres;
1490 static HRESULT String_localeCompare(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1491 jsval_t *r)
1493 FIXME("\n");
1494 return E_NOTIMPL;
1497 static void String_destructor(jsdisp_t *dispex)
1499 StringInstance *This = string_from_jsdisp(dispex);
1501 jsstr_release(This->str);
1502 free(This);
1505 static unsigned String_idx_length(jsdisp_t *jsdisp)
1507 StringInstance *string = string_from_jsdisp(jsdisp);
1510 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1511 * Newer jscript.dll versions implement it on string type, not class,
1512 * which is not how it should work according to spec. IE9 implements it
1513 * properly, but it uses its own JavaScript engine inside MSHTML. We
1514 * implement it here, but in the way IE9 and spec work.
1516 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1519 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1521 StringInstance *string = string_from_jsdisp(jsdisp);
1522 jsstr_t *ret;
1524 ret = jsstr_substr(string->str, idx, 1);
1525 if(!ret)
1526 return E_OUTOFMEMORY;
1528 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1530 *r = jsval_string(ret);
1531 return S_OK;
1534 static const builtin_prop_t String_props[] = {
1535 {L"anchor", String_anchor, PROPF_METHOD|1},
1536 {L"big", String_big, PROPF_METHOD},
1537 {L"blink", String_blink, PROPF_METHOD},
1538 {L"bold", String_bold, PROPF_METHOD},
1539 {L"charAt", String_charAt, PROPF_METHOD|1},
1540 {L"charCodeAt", String_charCodeAt, PROPF_METHOD|1},
1541 {L"concat", String_concat, PROPF_METHOD|1},
1542 {L"fixed", String_fixed, PROPF_METHOD},
1543 {L"fontcolor", String_fontcolor, PROPF_METHOD|1},
1544 {L"fontsize", String_fontsize, PROPF_METHOD|1},
1545 {L"indexOf", String_indexOf, PROPF_METHOD|2},
1546 {L"italics", String_italics, PROPF_METHOD},
1547 {L"lastIndexOf", String_lastIndexOf, PROPF_METHOD|2},
1548 {L"length", NULL,0, String_get_length},
1549 {L"link", String_link, PROPF_METHOD|1},
1550 {L"localeCompare", String_localeCompare, PROPF_METHOD|1},
1551 {L"match", String_match, PROPF_METHOD|1},
1552 {L"replace", String_replace, PROPF_METHOD|1},
1553 {L"search", String_search, PROPF_METHOD},
1554 {L"slice", String_slice, PROPF_METHOD},
1555 {L"small", String_small, PROPF_METHOD},
1556 {L"split", String_split, PROPF_METHOD|2},
1557 {L"strike", String_strike, PROPF_METHOD},
1558 {L"sub", String_sub, PROPF_METHOD},
1559 {L"substr", String_substr, PROPF_METHOD|2},
1560 {L"substring", String_substring, PROPF_METHOD|2},
1561 {L"sup", String_sup, PROPF_METHOD},
1562 {L"toLocaleLowerCase", String_toLocaleLowerCase, PROPF_METHOD},
1563 {L"toLocaleUpperCase", String_toLocaleUpperCase, PROPF_METHOD},
1564 {L"toLowerCase", String_toLowerCase, PROPF_METHOD},
1565 {L"toString", String_toString, PROPF_METHOD},
1566 {L"toUpperCase", String_toUpperCase, PROPF_METHOD},
1567 {L"trim", String_trim, PROPF_ES5|PROPF_METHOD},
1568 {L"valueOf", String_valueOf, PROPF_METHOD}
1571 static const builtin_info_t String_info = {
1572 JSCLASS_STRING,
1573 NULL,
1574 ARRAY_SIZE(String_props),
1575 String_props,
1576 String_destructor,
1577 NULL
1580 static const builtin_prop_t StringInst_props[] = {
1581 {L"length", NULL,0, String_get_length}
1584 static const builtin_info_t StringInst_info = {
1585 JSCLASS_STRING,
1586 NULL,
1587 ARRAY_SIZE(StringInst_props),
1588 StringInst_props,
1589 String_destructor,
1590 NULL,
1591 String_idx_length,
1592 String_idx_get
1595 /* ECMA-262 3rd Edition 15.5.3.2 */
1596 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, jsval_t vthis, WORD flags,
1597 unsigned argc, jsval_t *argv, jsval_t *r)
1599 WCHAR *ret_str;
1600 UINT32 i, code;
1601 jsstr_t *ret;
1602 HRESULT hres;
1604 TRACE("\n");
1606 ret = jsstr_alloc_buf(argc, &ret_str);
1607 if(!ret)
1608 return E_OUTOFMEMORY;
1610 for(i=0; i<argc; i++) {
1611 hres = to_uint32(ctx, argv[i], &code);
1612 if(FAILED(hres)) {
1613 jsstr_release(ret);
1614 return hres;
1617 ret_str[i] = code;
1620 if(r)
1621 *r = jsval_string(ret);
1622 else
1623 jsstr_release(ret);
1624 return S_OK;
1627 static HRESULT StringConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
1628 jsval_t *r)
1630 HRESULT hres = S_OK;
1632 TRACE("\n");
1634 switch(flags) {
1635 case INVOKE_FUNC: {
1636 jsstr_t *str;
1638 if(argc) {
1639 hres = to_string(ctx, argv[0], &str);
1640 if(FAILED(hres))
1641 return hres;
1642 }else {
1643 str = jsstr_empty();
1646 if(r) *r = jsval_string(str);
1647 else jsstr_release(str);
1648 break;
1650 case DISPATCH_CONSTRUCT: {
1651 jsstr_t *str;
1652 jsdisp_t *ret;
1654 if(argc) {
1655 hres = to_string(ctx, argv[0], &str);
1656 if(FAILED(hres))
1657 return hres;
1658 }else {
1659 str = jsstr_empty();
1662 if(r) {
1663 hres = create_string(ctx, str, &ret);
1664 if(SUCCEEDED(hres)) *r = jsval_obj(ret);
1666 jsstr_release(str);
1667 return hres;
1670 default:
1671 FIXME("unimplemented flags: %x\n", flags);
1672 return E_NOTIMPL;
1675 return S_OK;
1678 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1680 StringInstance *string;
1681 HRESULT hres;
1683 string = calloc(1, sizeof(StringInstance));
1684 if(!string)
1685 return E_OUTOFMEMORY;
1687 if(object_prototype)
1688 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1689 else
1690 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1691 if(FAILED(hres)) {
1692 free(string);
1693 return hres;
1696 string->str = jsstr_addref(str);
1697 *ret = string;
1698 return S_OK;
1701 static const builtin_prop_t StringConstr_props[] = {
1702 {L"fromCharCode", StringConstr_fromCharCode, PROPF_METHOD},
1705 static const builtin_info_t StringConstr_info = {
1706 JSCLASS_FUNCTION,
1707 Function_value,
1708 ARRAY_SIZE(StringConstr_props),
1709 StringConstr_props,
1710 NULL,
1711 NULL
1714 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1716 StringInstance *string;
1717 HRESULT hres;
1719 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1720 if(FAILED(hres))
1721 return hres;
1723 hres = create_builtin_constructor(ctx, StringConstr_value, L"String", &StringConstr_info,
1724 PROPF_CONSTR|1, &string->dispex, ret);
1726 jsdisp_release(&string->dispex);
1727 return hres;
1730 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1732 StringInstance *string;
1733 HRESULT hres;
1735 hres = string_alloc(ctx, NULL, str, &string);
1736 if(FAILED(hres))
1737 return hres;
1739 *ret = &string->dispex;
1740 return S_OK;