winegstreamer: Move seeking to the Unix library.
[wine.git] / dlls / jscript / string.c
bloba8bd77dc3982b29d605e45320a2f870602bd4f41
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_from_vdisp(vdisp_t *vdisp)
41 return string_from_jsdisp(vdisp->u.jsdisp);
44 static inline StringInstance *string_this(vdisp_t *jsthis)
46 return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL;
49 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **val)
51 StringInstance *string;
53 if((string = string_this(jsthis))) {
54 *val = jsstr_addref(string->str);
55 return S_OK;
58 return to_string(ctx, jsval_disp(jsthis->u.disp), val);
61 static HRESULT get_string_flat_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **jsval, const WCHAR **val)
63 HRESULT hres;
65 hres = get_string_val(ctx, jsthis, jsval);
66 if(FAILED(hres))
67 return hres;
69 *val = jsstr_flatten(*jsval);
70 if(*val)
71 return S_OK;
73 jsstr_release(*jsval);
74 return E_OUTOFMEMORY;
77 static HRESULT String_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
79 StringInstance *string = string_from_jsdisp(jsthis);
81 TRACE("%p\n", jsthis);
83 *r = jsval_number(jsstr_length(string->str));
84 return S_OK;
87 static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r)
89 StringInstance *string;
91 if(!(string = string_this(jsthis))) {
92 WARN("this is not a string object\n");
93 return E_FAIL;
96 if(r)
97 *r = jsval_string(jsstr_addref(string->str));
98 return S_OK;
101 /* ECMA-262 3rd Edition 15.5.4.2 */
102 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
103 jsval_t *r)
105 TRACE("\n");
107 return stringobj_to_string(jsthis, r);
110 /* ECMA-262 3rd Edition 15.5.4.2 */
111 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
112 jsval_t *r)
114 TRACE("\n");
116 return stringobj_to_string(jsthis, r);
119 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t *r, const WCHAR *tagname)
121 unsigned tagname_len;
122 jsstr_t *str, *ret;
123 WCHAR *ptr;
124 HRESULT hres;
126 hres = get_string_val(ctx, jsthis, &str);
127 if(FAILED(hres))
128 return hres;
130 if(!r) {
131 jsstr_release(str);
132 return S_OK;
135 tagname_len = lstrlenW(tagname);
137 ret = jsstr_alloc_buf(jsstr_length(str) + 2*tagname_len + 5, &ptr);
138 if(!ret) {
139 jsstr_release(str);
140 return E_OUTOFMEMORY;
143 *ptr++ = '<';
144 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
145 ptr += tagname_len;
146 *ptr++ = '>';
148 ptr += jsstr_flush(str, ptr);
149 jsstr_release(str);
151 *ptr++ = '<';
152 *ptr++ = '/';
153 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
154 ptr += tagname_len;
155 *ptr = '>';
157 *r = jsval_string(ret);
158 return S_OK;
161 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, unsigned argc, jsval_t *argv, jsval_t *r,
162 const WCHAR *tagname, const WCHAR *attrname)
164 jsstr_t *str, *attr_value = NULL;
165 HRESULT hres;
167 hres = get_string_val(ctx, jsthis, &str);
168 if(FAILED(hres))
169 return hres;
171 if(argc) {
172 hres = to_string(ctx, argv[0], &attr_value);
173 if(FAILED(hres)) {
174 jsstr_release(str);
175 return hres;
177 }else {
178 attr_value = jsstr_undefined();
181 if(r) {
182 unsigned attrname_len = lstrlenW(attrname);
183 unsigned tagname_len = lstrlenW(tagname);
184 jsstr_t *ret;
185 WCHAR *ptr;
187 ret = jsstr_alloc_buf(2*tagname_len + attrname_len + jsstr_length(attr_value) + jsstr_length(str) + 9, &ptr);
188 if(ret) {
189 *ptr++ = '<';
190 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
191 ptr += tagname_len;
192 *ptr++ = ' ';
193 memcpy(ptr, attrname, attrname_len*sizeof(WCHAR));
194 ptr += attrname_len;
195 *ptr++ = '=';
196 *ptr++ = '"';
197 ptr += jsstr_flush(attr_value, ptr);
198 *ptr++ = '"';
199 *ptr++ = '>';
200 ptr += jsstr_flush(str, ptr);
202 *ptr++ = '<';
203 *ptr++ = '/';
204 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
205 ptr += tagname_len;
206 *ptr = '>';
208 *r = jsval_string(ret);
209 }else {
210 hres = E_OUTOFMEMORY;
214 jsstr_release(attr_value);
215 jsstr_release(str);
216 return hres;
219 static HRESULT String_anchor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
220 jsval_t *r)
222 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, L"A", L"NAME");
225 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
226 jsval_t *r)
228 return do_attributeless_tag_format(ctx, jsthis, r, L"BIG");
231 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
232 jsval_t *r)
234 return do_attributeless_tag_format(ctx, jsthis, r, L"BLINK");
237 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
238 jsval_t *r)
240 return do_attributeless_tag_format(ctx, jsthis, r, L"B");
243 /* ECMA-262 3rd Edition 15.5.4.5 */
244 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
245 jsval_t *r)
247 jsstr_t *str, *ret;
248 INT pos = 0;
249 HRESULT hres;
251 TRACE("\n");
253 hres = get_string_val(ctx, jsthis, &str);
254 if(FAILED(hres))
255 return hres;
257 if(argc) {
258 double d;
260 hres = to_integer(ctx, argv[0], &d);
261 if(FAILED(hres)) {
262 jsstr_release(str);
263 return hres;
265 pos = is_int32(d) ? d : -1;
268 if(!r) {
269 jsstr_release(str);
270 return S_OK;
273 if(0 <= pos && pos < jsstr_length(str)) {
274 ret = jsstr_substr(str, pos, 1);
275 if(!ret)
276 return E_OUTOFMEMORY;
277 }else {
278 ret = jsstr_empty();
281 *r = jsval_string(ret);
282 return S_OK;
285 /* ECMA-262 3rd Edition 15.5.4.5 */
286 static HRESULT String_charCodeAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
287 jsval_t *r)
289 jsstr_t *str;
290 DWORD idx = 0;
291 HRESULT hres;
293 TRACE("\n");
295 hres = get_string_val(ctx, jsthis, &str);
296 if(FAILED(hres))
297 return hres;
299 if(argc > 0) {
300 double d;
302 hres = to_integer(ctx, argv[0], &d);
303 if(FAILED(hres)) {
304 jsstr_release(str);
305 return hres;
308 if(!is_int32(d) || d < 0 || d >= jsstr_length(str)) {
309 jsstr_release(str);
310 if(r)
311 *r = jsval_number(NAN);
312 return S_OK;
315 idx = d;
318 if(r) {
319 WCHAR c;
320 jsstr_extract(str, idx, 1, &c);
321 *r = jsval_number(c);
324 jsstr_release(str);
325 return S_OK;
328 /* ECMA-262 3rd Edition 15.5.4.6 */
329 static HRESULT String_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
330 jsval_t *r)
332 jsstr_t *ret = NULL, *str;
333 HRESULT hres;
335 TRACE("\n");
337 hres = get_string_val(ctx, jsthis, &str);
338 if(FAILED(hres))
339 return hres;
341 switch(argc) {
342 case 0:
343 ret = str;
344 break;
345 case 1: {
346 jsstr_t *arg_str;
348 hres = to_string(ctx, argv[0], &arg_str);
349 if(FAILED(hres)) {
350 jsstr_release(str);
351 return hres;
354 ret = jsstr_concat(str, arg_str);
355 jsstr_release(str);
356 if(!ret)
357 return E_OUTOFMEMORY;
358 break;
360 default: {
361 const unsigned str_cnt = argc+1;
362 unsigned len = 0, i;
363 jsstr_t **strs;
364 WCHAR *ptr;
366 strs = heap_alloc_zero(str_cnt * sizeof(*strs));
367 if(!strs) {
368 jsstr_release(str);
369 return E_OUTOFMEMORY;
372 strs[0] = str;
373 for(i=0; i < argc; i++) {
374 hres = to_string(ctx, argv[i], strs+i+1);
375 if(FAILED(hres))
376 break;
379 if(SUCCEEDED(hres)) {
380 for(i=0; i < str_cnt; i++) {
381 len += jsstr_length(strs[i]);
382 if(len > JSSTR_MAX_LENGTH) {
383 hres = E_OUTOFMEMORY;
384 break;
388 if(SUCCEEDED(hres)) {
389 ret = jsstr_alloc_buf(len, &ptr);
390 if(ret) {
391 for(i=0; i < str_cnt; i++)
392 ptr += jsstr_flush(strs[i], ptr);
393 }else {
394 hres = E_OUTOFMEMORY;
399 while(i--)
400 jsstr_release(strs[i]);
401 heap_free(strs);
402 if(FAILED(hres))
403 return hres;
407 if(r)
408 *r = jsval_string(ret);
409 else
410 jsstr_release(ret);
411 return S_OK;
414 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
415 jsval_t *r)
417 return do_attributeless_tag_format(ctx, jsthis, r, L"TT");
420 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
421 jsval_t *r)
423 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, L"FONT", L"COLOR");
426 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
427 jsval_t *r)
429 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, L"FONT", L"SIZE");
432 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
433 jsval_t *r)
435 unsigned pos = 0, search_len, length;
436 jsstr_t *search_jsstr, *jsstr;
437 const WCHAR *search_str, *str;
438 INT ret = -1;
439 HRESULT hres;
441 TRACE("\n");
443 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
444 if(FAILED(hres))
445 return hres;
447 if(!argc) {
448 if(r)
449 *r = jsval_number(-1);
450 jsstr_release(jsstr);
451 return S_OK;
454 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
455 if(FAILED(hres)) {
456 jsstr_release(jsstr);
457 return hres;
460 search_len = jsstr_length(search_jsstr);
461 length = jsstr_length(jsstr);
463 if(argc >= 2) {
464 double d;
466 hres = to_integer(ctx, argv[1], &d);
467 if(SUCCEEDED(hres) && d > 0.0)
468 pos = is_int32(d) ? min(length, d) : length;
471 if(SUCCEEDED(hres) && length >= search_len) {
472 const WCHAR *end = str+length-search_len;
473 const WCHAR *ptr;
475 for(ptr = str+pos; ptr <= end; ptr++) {
476 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
477 ret = ptr-str;
478 break;
483 jsstr_release(search_jsstr);
484 jsstr_release(jsstr);
485 if(FAILED(hres))
486 return hres;
488 if(r)
489 *r = jsval_number(ret);
490 return S_OK;
493 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
494 jsval_t *r)
496 return do_attributeless_tag_format(ctx, jsthis, r, L"I");
499 /* ECMA-262 3rd Edition 15.5.4.8 */
500 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
501 jsval_t *r)
503 unsigned pos = 0, search_len, length;
504 jsstr_t *search_jsstr, *jsstr;
505 const WCHAR *search_str, *str;
506 INT ret = -1;
507 HRESULT hres;
509 TRACE("\n");
511 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
512 if(FAILED(hres))
513 return hres;
515 if(!argc) {
516 if(r)
517 *r = jsval_number(-1);
518 jsstr_release(jsstr);
519 return S_OK;
522 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
523 if(FAILED(hres)) {
524 jsstr_release(jsstr);
525 return hres;
528 search_len = jsstr_length(search_jsstr);
529 length = jsstr_length(jsstr);
531 if(argc >= 2) {
532 double d;
534 hres = to_integer(ctx, argv[1], &d);
535 if(SUCCEEDED(hres) && d > 0)
536 pos = is_int32(d) ? min(length, d) : length;
537 }else {
538 pos = length;
541 if(SUCCEEDED(hres) && length >= search_len) {
542 const WCHAR *ptr;
544 for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) {
545 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
546 ret = ptr-str;
547 break;
552 jsstr_release(search_jsstr);
553 jsstr_release(jsstr);
554 if(FAILED(hres))
555 return hres;
557 if(r)
558 *r = jsval_number(ret);
559 return S_OK;
562 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
563 jsval_t *r)
565 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, L"A", L"HREF");
568 /* ECMA-262 3rd Edition 15.5.4.10 */
569 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
570 jsval_t *r)
572 jsdisp_t *regexp = NULL;
573 jsstr_t *str;
574 HRESULT hres;
576 TRACE("\n");
578 if(!argc) {
579 if(r)
580 *r = jsval_null();
581 return S_OK;
584 if(is_object_instance(argv[0])) {
585 regexp = iface_to_jsdisp(get_object(argv[0]));
586 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
587 jsdisp_release(regexp);
588 regexp = NULL;
592 if(!regexp) {
593 jsstr_t *match_str;
595 hres = to_string(ctx, argv[0], &match_str);
596 if(FAILED(hres))
597 return hres;
599 hres = create_regexp(ctx, match_str, 0, &regexp);
600 jsstr_release(match_str);
601 if(FAILED(hres))
602 return hres;
605 hres = get_string_val(ctx, jsthis, &str);
606 if(SUCCEEDED(hres))
607 hres = regexp_string_match(ctx, regexp, str, r);
609 jsdisp_release(regexp);
610 jsstr_release(str);
611 return hres;
614 typedef struct {
615 WCHAR *buf;
616 DWORD size;
617 DWORD len;
618 } strbuf_t;
620 static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len)
622 WCHAR *new_buf;
623 DWORD new_size;
625 if(len <= buf->size)
626 return TRUE;
628 new_size = buf->size ? buf->size<<1 : 16;
629 if(new_size < len)
630 new_size = len;
631 if(buf->buf)
632 new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
633 else
634 new_buf = heap_alloc(new_size*sizeof(WCHAR));
635 if(!new_buf)
636 return FALSE;
638 buf->buf = new_buf;
639 buf->size = new_size;
640 return TRUE;
643 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
645 if(!len)
646 return S_OK;
648 if(!strbuf_ensure_size(buf, buf->len+len))
649 return E_OUTOFMEMORY;
651 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
652 buf->len += len;
653 return S_OK;
656 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str)
658 if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str)))
659 return E_OUTOFMEMORY;
661 jsstr_flush(str, buf->buf+buf->len);
662 buf->len += jsstr_length(str);
663 return S_OK;
666 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
667 jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret)
669 jsval_t *argv;
670 unsigned argc;
671 jsval_t val;
672 jsstr_t *tmp_str;
673 DWORD i;
674 HRESULT hres = S_OK;
676 argc = match->paren_count+3;
677 argv = heap_alloc_zero(sizeof(*argv)*argc);
678 if(!argv)
679 return E_OUTOFMEMORY;
681 tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
682 if(!tmp_str)
683 hres = E_OUTOFMEMORY;
684 argv[0] = jsval_string(tmp_str);
686 if(SUCCEEDED(hres)) {
687 for(i=0; i < match->paren_count; i++) {
688 if(match->parens[i].index != -1)
689 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length);
690 else
691 tmp_str = jsstr_empty();
692 if(!tmp_str) {
693 hres = E_OUTOFMEMORY;
694 break;
696 argv[i+1] = jsval_string(tmp_str);
700 if(SUCCEEDED(hres)) {
701 argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len);
702 argv[match->paren_count+2] = jsval_string(jsstr);
705 if(SUCCEEDED(hres))
706 hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val);
708 for(i=0; i <= match->paren_count; i++)
709 jsstr_release(get_string(argv[i]));
710 heap_free(argv);
712 if(FAILED(hres))
713 return hres;
715 hres = to_string(ctx, val, ret);
716 jsval_release(val);
717 return hres;
720 /* ECMA-262 3rd Edition 15.5.4.11 */
721 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
722 jsval_t *r)
724 const WCHAR *str, *match_str = NULL, *rep_str = NULL;
725 jsstr_t *rep_jsstr, *match_jsstr, *jsstr;
726 jsdisp_t *rep_func = NULL, *regexp = NULL;
727 match_state_t *match = NULL, last_match = {0};
728 strbuf_t ret = {NULL,0,0};
729 DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
730 DWORD rep_len=0;
731 HRESULT hres = S_OK;
733 TRACE("\n");
735 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
736 if(FAILED(hres))
737 return hres;
739 if(!argc) {
740 if(r)
741 *r = jsval_string(jsstr);
742 else
743 jsstr_release(jsstr);
744 return S_OK;
747 if(is_object_instance(argv[0])) {
748 regexp = iface_to_jsdisp(get_object(argv[0]));
749 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
750 jsdisp_release(regexp);
751 regexp = NULL;
755 if(!regexp) {
756 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
757 if(FAILED(hres)) {
758 jsstr_release(jsstr);
759 return hres;
763 if(argc >= 2) {
764 if(is_object_instance(argv[1])) {
765 rep_func = iface_to_jsdisp(get_object(argv[1]));
766 if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
767 jsdisp_release(rep_func);
768 rep_func = NULL;
772 if(!rep_func) {
773 hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str);
774 if(SUCCEEDED(hres))
775 rep_len = jsstr_length(rep_jsstr);
779 if(SUCCEEDED(hres)) {
780 const WCHAR *ecp = str;
782 while(1) {
783 if(regexp) {
784 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match);
785 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
787 if(hres == S_FALSE) {
788 hres = S_OK;
789 break;
791 if(FAILED(hres))
792 break;
794 last_match.cp = match->cp;
795 last_match.match_len = match->match_len;
796 }else {
797 if(re_flags & REM_ALLOC_RESULT) {
798 re_flags &= ~REM_ALLOC_RESULT;
799 match = &last_match;
800 match->cp = str;
803 match->cp = wcsstr(match->cp, match_str);
804 if(!match->cp)
805 break;
806 match->match_len = jsstr_length(match_jsstr);
807 match->cp += match->match_len;
810 hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
811 ecp = match->cp;
812 if(FAILED(hres))
813 break;
815 if(rep_func) {
816 jsstr_t *cstr;
818 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr);
819 if(FAILED(hres))
820 break;
822 hres = strbuf_append_jsstr(&ret, cstr);
823 jsstr_release(cstr);
824 if(FAILED(hres))
825 break;
826 }else if(rep_str && regexp) {
827 const WCHAR *ptr = rep_str, *ptr2;
829 while((ptr2 = wcschr(ptr, '$'))) {
830 hres = strbuf_append(&ret, ptr, ptr2-ptr);
831 if(FAILED(hres))
832 break;
834 switch(ptr2[1]) {
835 case '$':
836 hres = strbuf_append(&ret, ptr2, 1);
837 ptr = ptr2+2;
838 break;
839 case '&':
840 hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
841 ptr = ptr2+2;
842 break;
843 case '`':
844 hres = strbuf_append(&ret, str, match->cp-str-match->match_len);
845 ptr = ptr2+2;
846 break;
847 case '\'':
848 hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp);
849 ptr = ptr2+2;
850 break;
851 default: {
852 DWORD idx;
854 if(!is_digit(ptr2[1])) {
855 hres = strbuf_append(&ret, ptr2, 1);
856 ptr = ptr2+1;
857 break;
860 idx = ptr2[1] - '0';
861 if(is_digit(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
862 idx = idx*10 + (ptr[2]-'0');
863 ptr = ptr2+3;
864 }else if(idx && idx <= match->paren_count) {
865 ptr = ptr2+2;
866 }else {
867 hres = strbuf_append(&ret, ptr2, 1);
868 ptr = ptr2+1;
869 break;
872 if(match->parens[idx-1].index != -1)
873 hres = strbuf_append(&ret, str+match->parens[idx-1].index,
874 match->parens[idx-1].length);
878 if(FAILED(hres))
879 break;
882 if(SUCCEEDED(hres))
883 hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
884 if(FAILED(hres))
885 break;
886 }else if(rep_str) {
887 hres = strbuf_append(&ret, rep_str, rep_len);
888 if(FAILED(hres))
889 break;
890 }else {
891 hres = strbuf_append(&ret, L"undefined", ARRAY_SIZE(L"undefined")-1);
892 if(FAILED(hres))
893 break;
896 if(!regexp)
897 break;
898 else if(!match->match_len)
899 match->cp++;
902 if(SUCCEEDED(hres))
903 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
906 if(rep_func)
907 jsdisp_release(rep_func);
908 if(rep_str)
909 jsstr_release(rep_jsstr);
910 if(match_str)
911 jsstr_release(match_jsstr);
912 if(regexp)
913 heap_free(match);
915 if(SUCCEEDED(hres) && last_match.cp && regexp) {
916 jsstr_release(ctx->last_match);
917 ctx->last_match = jsstr_addref(jsstr);
918 ctx->last_match_index = last_match.cp-str-last_match.match_len;
919 ctx->last_match_length = last_match.match_len;
922 if(regexp)
923 jsdisp_release(regexp);
924 jsstr_release(jsstr);
926 if(SUCCEEDED(hres) && r) {
927 jsstr_t *ret_str;
929 ret_str = jsstr_alloc_len(ret.buf, ret.len);
930 if(!ret_str)
931 return E_OUTOFMEMORY;
933 TRACE("= %s\n", debugstr_jsstr(ret_str));
934 *r = jsval_string(ret_str);
937 heap_free(ret.buf);
938 return hres;
941 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
942 jsval_t *r)
944 jsdisp_t *regexp = NULL;
945 const WCHAR *str;
946 jsstr_t *jsstr;
947 match_state_t match, *match_ptr = &match;
948 HRESULT hres;
950 TRACE("\n");
952 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
953 if(FAILED(hres))
954 return hres;
956 if(!argc) {
957 if(r)
958 *r = jsval_null();
959 jsstr_release(jsstr);
960 return S_OK;
963 if(is_object_instance(argv[0])) {
964 regexp = iface_to_jsdisp(get_object(argv[0]));
965 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
966 jsdisp_release(regexp);
967 regexp = NULL;
971 if(!regexp) {
972 hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
973 if(FAILED(hres)) {
974 jsstr_release(jsstr);
975 return hres;
979 match.cp = str;
980 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
981 jsstr_release(jsstr);
982 jsdisp_release(regexp);
983 if(FAILED(hres))
984 return hres;
986 if(r)
987 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
988 return S_OK;
991 /* ECMA-262 3rd Edition 15.5.4.13 */
992 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
993 jsval_t *r)
995 int start=0, end, length;
996 jsstr_t *str;
997 double d;
998 HRESULT hres;
1000 TRACE("\n");
1002 hres = get_string_val(ctx, jsthis, &str);
1003 if(FAILED(hres))
1004 return hres;
1006 length = jsstr_length(str);
1007 if(argc) {
1008 hres = to_integer(ctx, argv[0], &d);
1009 if(FAILED(hres)) {
1010 jsstr_release(str);
1011 return hres;
1014 if(is_int32(d)) {
1015 start = d;
1016 if(start < 0) {
1017 start = length + start;
1018 if(start < 0)
1019 start = 0;
1020 }else if(start > length) {
1021 start = length;
1023 }else if(d > 0) {
1024 start = length;
1028 if(argc >= 2) {
1029 hres = to_integer(ctx, argv[1], &d);
1030 if(FAILED(hres)) {
1031 jsstr_release(str);
1032 return hres;
1035 if(is_int32(d)) {
1036 end = d;
1037 if(end < 0) {
1038 end = length + end;
1039 if(end < 0)
1040 end = 0;
1041 }else if(end > length) {
1042 end = length;
1044 }else {
1045 end = d < 0.0 ? 0 : length;
1047 }else {
1048 end = length;
1051 if(end < start)
1052 end = start;
1054 if(r) {
1055 jsstr_t *retstr = jsstr_substr(str, start, end-start);
1056 if(!retstr) {
1057 jsstr_release(str);
1058 return E_OUTOFMEMORY;
1061 *r = jsval_string(retstr);
1064 jsstr_release(str);
1065 return S_OK;
1068 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1069 jsval_t *r)
1071 return do_attributeless_tag_format(ctx, jsthis, r, L"SMALL");
1074 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1075 jsval_t *r)
1077 match_state_t match_result, *match_ptr = &match_result;
1078 size_t length, i = 0, match_len = 0;
1079 const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1080 unsigned limit = ~0u;
1081 jsdisp_t *array, *regexp = NULL;
1082 jsstr_t *jsstr, *match_jsstr, *tmp_str;
1083 HRESULT hres;
1085 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
1086 if(FAILED(hres))
1087 return hres;
1088 length = jsstr_length(jsstr);
1090 TRACE("%s\n", debugstr_wn(str, length));
1092 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) {
1093 if(!r)
1094 return S_OK;
1096 hres = create_array(ctx, 0, &array);
1097 if(FAILED(hres))
1098 return hres;
1100 /* NOTE: according to spec, we should respect limit argument here (if provided).
1101 * We have a test showing that it's broken in native IE. */
1102 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr));
1103 if(FAILED(hres)) {
1104 jsdisp_release(array);
1105 return hres;
1108 *r = jsval_obj(array);
1109 return S_OK;
1112 if(argc > 1 && !is_undefined(argv[1])) {
1113 hres = to_uint32(ctx, argv[1], &limit);
1114 if(FAILED(hres)) {
1115 jsstr_release(jsstr);
1116 return hres;
1120 if(is_object_instance(argv[0])) {
1121 regexp = iface_to_jsdisp(get_object(argv[0]));
1122 if(regexp) {
1123 if(!is_class(regexp, JSCLASS_REGEXP)) {
1124 jsdisp_release(regexp);
1125 regexp = NULL;
1130 if(!regexp) {
1131 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1132 if(FAILED(hres)) {
1133 jsstr_release(jsstr);
1134 return hres;
1137 match_len = jsstr_length(match_jsstr);
1138 if(!match_len) {
1139 jsstr_release(match_jsstr);
1140 match_str = NULL;
1144 hres = create_array(ctx, 0, &array);
1146 if(SUCCEEDED(hres)) {
1147 ptr = str;
1148 match_result.cp = str;
1149 while(i < limit) {
1150 if(regexp) {
1151 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1152 if(hres != S_OK)
1153 break;
1154 TRACE("got match %d %d\n", (int)(match_result.cp - match_result.match_len - str), match_result.match_len);
1155 if(!match_result.match_len) {
1156 /* If an empty string is matched, prevent including any match in the result */
1157 if(!length) {
1158 limit = 0;
1159 break;
1161 if(match_result.cp == ptr) {
1162 match_result.cp++;
1163 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1164 if(hres != S_OK)
1165 break;
1166 TRACE("retried, got match %d %d\n", (int)(match_result.cp - match_result.match_len - str),
1167 match_result.match_len);
1169 if(!match_result.match_len && match_result.cp == str + length)
1170 break;
1172 ptr2 = match_result.cp - match_result.match_len;
1173 }else if(match_str) {
1174 ptr2 = wcsstr(ptr, match_str);
1175 if(!ptr2)
1176 break;
1177 }else {
1178 if(!*ptr)
1179 break;
1180 ptr2 = ptr+1;
1183 if(!regexp || ptr2 > ptr || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1184 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1185 if(!tmp_str) {
1186 hres = E_OUTOFMEMORY;
1187 break;
1190 hres = jsdisp_propput_idx(array, i++, jsval_string(tmp_str));
1191 jsstr_release(tmp_str);
1192 if(FAILED(hres))
1193 break;
1196 if(regexp)
1197 ptr = match_result.cp;
1198 else if(match_str)
1199 ptr = ptr2 + match_len;
1200 else
1201 ptr++;
1205 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1206 DWORD len = (str+length) - ptr;
1208 if(len || match_str || !length || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1209 tmp_str = jsstr_alloc_len(ptr, len);
1211 if(tmp_str) {
1212 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1213 jsstr_release(tmp_str);
1214 }else {
1215 hres = E_OUTOFMEMORY;
1220 if(regexp)
1221 jsdisp_release(regexp);
1222 if(match_str)
1223 jsstr_release(match_jsstr);
1224 jsstr_release(jsstr);
1226 if(SUCCEEDED(hres) && r)
1227 *r = jsval_obj(array);
1228 else
1229 jsdisp_release(array);
1231 return hres;
1234 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1235 jsval_t *r)
1237 return do_attributeless_tag_format(ctx, jsthis, r, L"STRIKE");
1240 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1241 jsval_t *r)
1243 return do_attributeless_tag_format(ctx, jsthis, r, L"SUB");
1246 /* ECMA-262 3rd Edition 15.5.4.15 */
1247 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1248 jsval_t *r)
1250 INT start=0, end, length;
1251 jsstr_t *str;
1252 double d;
1253 HRESULT hres;
1255 TRACE("\n");
1257 hres = get_string_val(ctx, jsthis, &str);
1258 if(FAILED(hres))
1259 return hres;
1261 length = jsstr_length(str);
1262 if(argc >= 1) {
1263 hres = to_integer(ctx, argv[0], &d);
1264 if(FAILED(hres)) {
1265 jsstr_release(str);
1266 return hres;
1269 if(d >= 0)
1270 start = is_int32(d) ? min(length, d) : length;
1273 if(argc >= 2) {
1274 hres = to_integer(ctx, argv[1], &d);
1275 if(FAILED(hres)) {
1276 jsstr_release(str);
1277 return hres;
1280 if(d >= 0)
1281 end = is_int32(d) ? min(length, d) : length;
1282 else
1283 end = 0;
1284 }else {
1285 end = length;
1288 if(start > end) {
1289 INT tmp = start;
1290 start = end;
1291 end = tmp;
1294 if(r) {
1295 jsstr_t *ret = jsstr_substr(str, start, end-start);
1296 if(ret)
1297 *r = jsval_string(ret);
1298 else
1299 hres = E_OUTOFMEMORY;
1301 jsstr_release(str);
1302 return hres;
1305 /* ECMA-262 3rd Edition B.2.3 */
1306 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1307 jsval_t *r)
1309 int start=0, len, length;
1310 jsstr_t *str;
1311 double d;
1312 HRESULT hres;
1314 TRACE("\n");
1316 hres = get_string_val(ctx, jsthis, &str);
1317 if(FAILED(hres))
1318 return hres;
1320 length = jsstr_length(str);
1321 if(argc >= 1) {
1322 hres = to_integer(ctx, argv[0], &d);
1323 if(FAILED(hres)) {
1324 jsstr_release(str);
1325 return hres;
1328 if(d >= 0)
1329 start = is_int32(d) ? min(length, d) : length;
1332 if(argc >= 2) {
1333 hres = to_integer(ctx, argv[1], &d);
1334 if(FAILED(hres)) {
1335 jsstr_release(str);
1336 return hres;
1339 if(d >= 0.0)
1340 len = is_int32(d) ? min(length-start, d) : length-start;
1341 else
1342 len = 0;
1343 }else {
1344 len = length-start;
1347 hres = S_OK;
1348 if(r) {
1349 jsstr_t *ret = jsstr_substr(str, start, len);
1350 if(ret)
1351 *r = jsval_string(ret);
1352 else
1353 hres = E_OUTOFMEMORY;
1356 jsstr_release(str);
1357 return hres;
1360 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1361 jsval_t *r)
1363 return do_attributeless_tag_format(ctx, jsthis, r, L"SUP");
1366 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1367 jsval_t *r)
1369 jsstr_t *str;
1370 HRESULT hres;
1372 TRACE("\n");
1374 hres = get_string_val(ctx, jsthis, &str);
1375 if(FAILED(hres))
1376 return hres;
1378 if(r) {
1379 unsigned len = jsstr_length(str);
1380 jsstr_t *ret;
1381 WCHAR *buf;
1383 ret = jsstr_alloc_buf(len, &buf);
1384 if(!ret) {
1385 jsstr_release(str);
1386 return E_OUTOFMEMORY;
1389 jsstr_flush(str, buf);
1390 for (; len--; buf++) *buf = towlower(*buf);
1392 *r = jsval_string(ret);
1394 jsstr_release(str);
1395 return S_OK;
1398 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1399 jsval_t *r)
1401 jsstr_t *str;
1402 HRESULT hres;
1404 TRACE("\n");
1406 hres = get_string_val(ctx, jsthis, &str);
1407 if(FAILED(hres))
1408 return hres;
1410 if(r) {
1411 unsigned len = jsstr_length(str);
1412 jsstr_t *ret;
1413 WCHAR *buf;
1415 ret = jsstr_alloc_buf(len, &buf);
1416 if(!ret) {
1417 jsstr_release(str);
1418 return E_OUTOFMEMORY;
1421 jsstr_flush(str, buf);
1422 for (; len--; buf++) *buf = towupper(*buf);
1424 *r = jsval_string(ret);
1426 jsstr_release(str);
1427 return S_OK;
1430 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1431 jsval_t *r)
1433 FIXME("\n");
1434 return E_NOTIMPL;
1437 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1438 jsval_t *r)
1440 FIXME("\n");
1441 return E_NOTIMPL;
1444 static HRESULT String_trim(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc,
1445 jsval_t *argv, jsval_t *r)
1447 const WCHAR *str, *begin, *end;
1448 jsstr_t *jsstr;
1449 unsigned len;
1450 HRESULT hres;
1452 hres = to_flat_string(ctx, jsval_disp(jsthis->u.disp), &jsstr, &str);
1453 if(FAILED(hres)) {
1454 WARN("to_flat_string failed: %08x\n", hres);
1455 return hres;
1457 len = jsstr_length(jsstr);
1458 TRACE("%s\n", debugstr_wn(str, len));
1460 for(begin = str, end = str + len; begin < end && iswspace(*begin); begin++);
1461 while(end > begin + 1 && iswspace(*(end-1))) end--;
1463 if(r) {
1464 jsstr_t *ret;
1466 if(begin == str && end == str + len)
1467 ret = jsstr_addref(jsstr);
1468 else
1469 ret = jsstr_alloc_len(begin, end - begin);
1470 if(ret)
1471 *r = jsval_string(ret);
1472 else
1473 hres = E_OUTOFMEMORY;
1475 jsstr_release(jsstr);
1476 return hres;
1479 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1480 jsval_t *r)
1482 FIXME("\n");
1483 return E_NOTIMPL;
1486 static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
1488 StringInstance *This = string_from_jsdisp(jsthis);
1490 TRACE("\n");
1492 *r = jsval_string(jsstr_addref(This->str));
1493 return S_OK;
1496 static void String_destructor(jsdisp_t *dispex)
1498 StringInstance *This = string_from_jsdisp(dispex);
1500 jsstr_release(This->str);
1501 heap_free(This);
1504 static unsigned String_idx_length(jsdisp_t *jsdisp)
1506 StringInstance *string = string_from_jsdisp(jsdisp);
1509 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1510 * Newer jscript.dll versions implement it on string type, not class,
1511 * which is not how it should work according to spec. IE9 implements it
1512 * properly, but it uses its own JavaScript engine inside MSHTML. We
1513 * implement it here, but in the way IE9 and spec work.
1515 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1518 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1520 StringInstance *string = string_from_jsdisp(jsdisp);
1521 jsstr_t *ret;
1523 ret = jsstr_substr(string->str, idx, 1);
1524 if(!ret)
1525 return E_OUTOFMEMORY;
1527 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1529 *r = jsval_string(ret);
1530 return S_OK;
1533 static const builtin_prop_t String_props[] = {
1534 {L"anchor", String_anchor, PROPF_METHOD|1},
1535 {L"big", String_big, PROPF_METHOD},
1536 {L"blink", String_blink, PROPF_METHOD},
1537 {L"bold", String_bold, PROPF_METHOD},
1538 {L"charAt", String_charAt, PROPF_METHOD|1},
1539 {L"charCodeAt", String_charCodeAt, PROPF_METHOD|1},
1540 {L"concat", String_concat, PROPF_METHOD|1},
1541 {L"fixed", String_fixed, PROPF_METHOD},
1542 {L"fontcolor", String_fontcolor, PROPF_METHOD|1},
1543 {L"fontsize", String_fontsize, PROPF_METHOD|1},
1544 {L"indexOf", String_indexOf, PROPF_METHOD|2},
1545 {L"italics", String_italics, PROPF_METHOD},
1546 {L"lastIndexOf", String_lastIndexOf, PROPF_METHOD|2},
1547 {L"length", NULL,0, String_get_length},
1548 {L"link", String_link, PROPF_METHOD|1},
1549 {L"localeCompare", String_localeCompare, PROPF_METHOD|1},
1550 {L"match", String_match, PROPF_METHOD|1},
1551 {L"replace", String_replace, PROPF_METHOD|1},
1552 {L"search", String_search, PROPF_METHOD},
1553 {L"slice", String_slice, PROPF_METHOD},
1554 {L"small", String_small, PROPF_METHOD},
1555 {L"split", String_split, PROPF_METHOD|2},
1556 {L"strike", String_strike, PROPF_METHOD},
1557 {L"sub", String_sub, PROPF_METHOD},
1558 {L"substr", String_substr, PROPF_METHOD|2},
1559 {L"substring", String_substring, PROPF_METHOD|2},
1560 {L"sup", String_sup, PROPF_METHOD},
1561 {L"toLocaleLowerCase", String_toLocaleLowerCase, PROPF_METHOD},
1562 {L"toLocaleUpperCase", String_toLocaleUpperCase, PROPF_METHOD},
1563 {L"toLowerCase", String_toLowerCase, PROPF_METHOD},
1564 {L"toString", String_toString, PROPF_METHOD},
1565 {L"toUpperCase", String_toUpperCase, PROPF_METHOD},
1566 {L"trim", String_trim, PROPF_ES5|PROPF_METHOD},
1567 {L"valueOf", String_valueOf, PROPF_METHOD}
1570 static const builtin_info_t String_info = {
1571 JSCLASS_STRING,
1572 {NULL, NULL,0, String_get_value},
1573 ARRAY_SIZE(String_props),
1574 String_props,
1575 String_destructor,
1576 NULL
1579 static const builtin_prop_t StringInst_props[] = {
1580 {L"length", NULL,0, String_get_length}
1583 static const builtin_info_t StringInst_info = {
1584 JSCLASS_STRING,
1585 {NULL, NULL,0, String_get_value},
1586 ARRAY_SIZE(StringInst_props),
1587 StringInst_props,
1588 String_destructor,
1589 NULL,
1590 String_idx_length,
1591 String_idx_get
1594 /* ECMA-262 3rd Edition 15.5.3.2 */
1595 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1596 unsigned argc, jsval_t *argv, jsval_t *r)
1598 WCHAR *ret_str;
1599 DWORD i, code;
1600 jsstr_t *ret;
1601 HRESULT hres;
1603 TRACE("\n");
1605 ret = jsstr_alloc_buf(argc, &ret_str);
1606 if(!ret)
1607 return E_OUTOFMEMORY;
1609 for(i=0; i<argc; i++) {
1610 hres = to_uint32(ctx, argv[i], &code);
1611 if(FAILED(hres)) {
1612 jsstr_release(ret);
1613 return hres;
1616 ret_str[i] = code;
1619 if(r)
1620 *r = jsval_string(ret);
1621 else
1622 jsstr_release(ret);
1623 return S_OK;
1626 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1627 jsval_t *r)
1629 HRESULT hres;
1631 TRACE("\n");
1633 switch(flags) {
1634 case INVOKE_FUNC: {
1635 jsstr_t *str;
1637 if(argc) {
1638 hres = to_string(ctx, argv[0], &str);
1639 if(FAILED(hres))
1640 return hres;
1641 }else {
1642 str = jsstr_empty();
1645 *r = jsval_string(str);
1646 break;
1648 case DISPATCH_CONSTRUCT: {
1649 jsstr_t *str;
1650 jsdisp_t *ret;
1652 if(argc) {
1653 hres = to_string(ctx, argv[0], &str);
1654 if(FAILED(hres))
1655 return hres;
1656 }else {
1657 str = jsstr_empty();
1660 hres = create_string(ctx, str, &ret);
1661 if (SUCCEEDED(hres)) *r = jsval_obj(ret);
1662 jsstr_release(str);
1663 return hres;
1666 default:
1667 FIXME("unimplemented flags: %x\n", flags);
1668 return E_NOTIMPL;
1671 return S_OK;
1674 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1676 StringInstance *string;
1677 HRESULT hres;
1679 string = heap_alloc_zero(sizeof(StringInstance));
1680 if(!string)
1681 return E_OUTOFMEMORY;
1683 if(object_prototype)
1684 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1685 else
1686 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1687 if(FAILED(hres)) {
1688 heap_free(string);
1689 return hres;
1692 string->str = jsstr_addref(str);
1693 *ret = string;
1694 return S_OK;
1697 static const builtin_prop_t StringConstr_props[] = {
1698 {L"fromCharCode", StringConstr_fromCharCode, PROPF_METHOD},
1701 static const builtin_info_t StringConstr_info = {
1702 JSCLASS_FUNCTION,
1703 DEFAULT_FUNCTION_VALUE,
1704 ARRAY_SIZE(StringConstr_props),
1705 StringConstr_props,
1706 NULL,
1707 NULL
1710 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1712 StringInstance *string;
1713 HRESULT hres;
1715 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1716 if(FAILED(hres))
1717 return hres;
1719 hres = create_builtin_constructor(ctx, StringConstr_value, L"String", &StringConstr_info,
1720 PROPF_CONSTR|1, &string->dispex, ret);
1722 jsdisp_release(&string->dispex);
1723 return hres;
1726 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1728 StringInstance *string;
1729 HRESULT hres;
1731 hres = string_alloc(ctx, NULL, str, &string);
1732 if(FAILED(hres))
1733 return hres;
1735 *ret = &string->dispex;
1736 return S_OK;