gdi32: Try to parse font names without FreeType.
[wine/zf.git] / dlls / jscript / string.c
blob3dd40e6744fbfeb5ae31af2dbab7cfb3a8c2f1c3
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 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
893 hres = strbuf_append(&ret, undefinedW, ARRAY_SIZE(undefinedW));
894 if(FAILED(hres))
895 break;
898 if(!regexp)
899 break;
900 else if(!match->match_len)
901 match->cp++;
904 if(SUCCEEDED(hres))
905 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
908 if(rep_func)
909 jsdisp_release(rep_func);
910 if(rep_str)
911 jsstr_release(rep_jsstr);
912 if(match_str)
913 jsstr_release(match_jsstr);
914 if(regexp)
915 heap_free(match);
917 if(SUCCEEDED(hres) && last_match.cp && regexp) {
918 jsstr_release(ctx->last_match);
919 ctx->last_match = jsstr_addref(jsstr);
920 ctx->last_match_index = last_match.cp-str-last_match.match_len;
921 ctx->last_match_length = last_match.match_len;
924 if(regexp)
925 jsdisp_release(regexp);
926 jsstr_release(jsstr);
928 if(SUCCEEDED(hres) && r) {
929 jsstr_t *ret_str;
931 ret_str = jsstr_alloc_len(ret.buf, ret.len);
932 if(!ret_str)
933 return E_OUTOFMEMORY;
935 TRACE("= %s\n", debugstr_jsstr(ret_str));
936 *r = jsval_string(ret_str);
939 heap_free(ret.buf);
940 return hres;
943 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
944 jsval_t *r)
946 jsdisp_t *regexp = NULL;
947 const WCHAR *str;
948 jsstr_t *jsstr;
949 match_state_t match, *match_ptr = &match;
950 HRESULT hres;
952 TRACE("\n");
954 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
955 if(FAILED(hres))
956 return hres;
958 if(!argc) {
959 if(r)
960 *r = jsval_null();
961 jsstr_release(jsstr);
962 return S_OK;
965 if(is_object_instance(argv[0])) {
966 regexp = iface_to_jsdisp(get_object(argv[0]));
967 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
968 jsdisp_release(regexp);
969 regexp = NULL;
973 if(!regexp) {
974 hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
975 if(FAILED(hres)) {
976 jsstr_release(jsstr);
977 return hres;
981 match.cp = str;
982 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
983 jsstr_release(jsstr);
984 jsdisp_release(regexp);
985 if(FAILED(hres))
986 return hres;
988 if(r)
989 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
990 return S_OK;
993 /* ECMA-262 3rd Edition 15.5.4.13 */
994 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
995 jsval_t *r)
997 int start=0, end, length;
998 jsstr_t *str;
999 double d;
1000 HRESULT hres;
1002 TRACE("\n");
1004 hres = get_string_val(ctx, jsthis, &str);
1005 if(FAILED(hres))
1006 return hres;
1008 length = jsstr_length(str);
1009 if(argc) {
1010 hres = to_integer(ctx, argv[0], &d);
1011 if(FAILED(hres)) {
1012 jsstr_release(str);
1013 return hres;
1016 if(is_int32(d)) {
1017 start = d;
1018 if(start < 0) {
1019 start = length + start;
1020 if(start < 0)
1021 start = 0;
1022 }else if(start > length) {
1023 start = length;
1025 }else if(d > 0) {
1026 start = length;
1030 if(argc >= 2) {
1031 hres = to_integer(ctx, argv[1], &d);
1032 if(FAILED(hres)) {
1033 jsstr_release(str);
1034 return hres;
1037 if(is_int32(d)) {
1038 end = d;
1039 if(end < 0) {
1040 end = length + end;
1041 if(end < 0)
1042 end = 0;
1043 }else if(end > length) {
1044 end = length;
1046 }else {
1047 end = d < 0.0 ? 0 : length;
1049 }else {
1050 end = length;
1053 if(end < start)
1054 end = start;
1056 if(r) {
1057 jsstr_t *retstr = jsstr_substr(str, start, end-start);
1058 if(!retstr) {
1059 jsstr_release(str);
1060 return E_OUTOFMEMORY;
1063 *r = jsval_string(retstr);
1066 jsstr_release(str);
1067 return S_OK;
1070 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1071 jsval_t *r)
1073 return do_attributeless_tag_format(ctx, jsthis, r, L"SMALL");
1076 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1077 jsval_t *r)
1079 match_state_t match_result, *match_ptr = &match_result;
1080 size_t length, i = 0, match_len = 0;
1081 const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1082 unsigned limit = ~0u;
1083 jsdisp_t *array, *regexp = NULL;
1084 jsstr_t *jsstr, *match_jsstr, *tmp_str;
1085 HRESULT hres;
1087 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
1088 if(FAILED(hres))
1089 return hres;
1090 length = jsstr_length(jsstr);
1092 TRACE("%s\n", debugstr_wn(str, length));
1094 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) {
1095 if(!r)
1096 return S_OK;
1098 hres = create_array(ctx, 0, &array);
1099 if(FAILED(hres))
1100 return hres;
1102 /* NOTE: according to spec, we should respect limit argument here (if provided).
1103 * We have a test showing that it's broken in native IE. */
1104 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr));
1105 if(FAILED(hres)) {
1106 jsdisp_release(array);
1107 return hres;
1110 *r = jsval_obj(array);
1111 return S_OK;
1114 if(argc > 1 && !is_undefined(argv[1])) {
1115 hres = to_uint32(ctx, argv[1], &limit);
1116 if(FAILED(hres)) {
1117 jsstr_release(jsstr);
1118 return hres;
1122 if(is_object_instance(argv[0])) {
1123 regexp = iface_to_jsdisp(get_object(argv[0]));
1124 if(regexp) {
1125 if(!is_class(regexp, JSCLASS_REGEXP)) {
1126 jsdisp_release(regexp);
1127 regexp = NULL;
1132 if(!regexp) {
1133 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1134 if(FAILED(hres)) {
1135 jsstr_release(jsstr);
1136 return hres;
1139 match_len = jsstr_length(match_jsstr);
1140 if(!match_len) {
1141 jsstr_release(match_jsstr);
1142 match_str = NULL;
1146 hres = create_array(ctx, 0, &array);
1148 if(SUCCEEDED(hres)) {
1149 ptr = str;
1150 match_result.cp = str;
1151 while(i < limit) {
1152 if(regexp) {
1153 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1154 if(hres != S_OK)
1155 break;
1156 TRACE("got match %d %d\n", (int)(match_result.cp - match_result.match_len - str), match_result.match_len);
1157 if(!match_result.match_len) {
1158 /* If an empty string is matched, prevent including any match in the result */
1159 if(!length) {
1160 limit = 0;
1161 break;
1163 if(match_result.cp == ptr) {
1164 match_result.cp++;
1165 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1166 if(hres != S_OK)
1167 break;
1168 TRACE("retried, got match %d %d\n", (int)(match_result.cp - match_result.match_len - str),
1169 match_result.match_len);
1171 if(!match_result.match_len && match_result.cp == str + length)
1172 break;
1174 ptr2 = match_result.cp - match_result.match_len;
1175 }else if(match_str) {
1176 ptr2 = wcsstr(ptr, match_str);
1177 if(!ptr2)
1178 break;
1179 }else {
1180 if(!*ptr)
1181 break;
1182 ptr2 = ptr+1;
1185 if(!regexp || ptr2 > ptr || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1186 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1187 if(!tmp_str) {
1188 hres = E_OUTOFMEMORY;
1189 break;
1192 hres = jsdisp_propput_idx(array, i++, jsval_string(tmp_str));
1193 jsstr_release(tmp_str);
1194 if(FAILED(hres))
1195 break;
1198 if(regexp)
1199 ptr = match_result.cp;
1200 else if(match_str)
1201 ptr = ptr2 + match_len;
1202 else
1203 ptr++;
1207 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1208 DWORD len = (str+length) - ptr;
1210 if(len || match_str || !length || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) {
1211 tmp_str = jsstr_alloc_len(ptr, len);
1213 if(tmp_str) {
1214 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1215 jsstr_release(tmp_str);
1216 }else {
1217 hres = E_OUTOFMEMORY;
1222 if(regexp)
1223 jsdisp_release(regexp);
1224 if(match_str)
1225 jsstr_release(match_jsstr);
1226 jsstr_release(jsstr);
1228 if(SUCCEEDED(hres) && r)
1229 *r = jsval_obj(array);
1230 else
1231 jsdisp_release(array);
1233 return hres;
1236 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1237 jsval_t *r)
1239 return do_attributeless_tag_format(ctx, jsthis, r, L"STRIKE");
1242 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1243 jsval_t *r)
1245 return do_attributeless_tag_format(ctx, jsthis, r, L"SUB");
1248 /* ECMA-262 3rd Edition 15.5.4.15 */
1249 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1250 jsval_t *r)
1252 INT start=0, end, length;
1253 jsstr_t *str;
1254 double d;
1255 HRESULT hres;
1257 TRACE("\n");
1259 hres = get_string_val(ctx, jsthis, &str);
1260 if(FAILED(hres))
1261 return hres;
1263 length = jsstr_length(str);
1264 if(argc >= 1) {
1265 hres = to_integer(ctx, argv[0], &d);
1266 if(FAILED(hres)) {
1267 jsstr_release(str);
1268 return hres;
1271 if(d >= 0)
1272 start = is_int32(d) ? min(length, d) : length;
1275 if(argc >= 2) {
1276 hres = to_integer(ctx, argv[1], &d);
1277 if(FAILED(hres)) {
1278 jsstr_release(str);
1279 return hres;
1282 if(d >= 0)
1283 end = is_int32(d) ? min(length, d) : length;
1284 else
1285 end = 0;
1286 }else {
1287 end = length;
1290 if(start > end) {
1291 INT tmp = start;
1292 start = end;
1293 end = tmp;
1296 if(r) {
1297 jsstr_t *ret = jsstr_substr(str, start, end-start);
1298 if(ret)
1299 *r = jsval_string(ret);
1300 else
1301 hres = E_OUTOFMEMORY;
1303 jsstr_release(str);
1304 return hres;
1307 /* ECMA-262 3rd Edition B.2.3 */
1308 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1309 jsval_t *r)
1311 int start=0, len, length;
1312 jsstr_t *str;
1313 double d;
1314 HRESULT hres;
1316 TRACE("\n");
1318 hres = get_string_val(ctx, jsthis, &str);
1319 if(FAILED(hres))
1320 return hres;
1322 length = jsstr_length(str);
1323 if(argc >= 1) {
1324 hres = to_integer(ctx, argv[0], &d);
1325 if(FAILED(hres)) {
1326 jsstr_release(str);
1327 return hres;
1330 if(d >= 0)
1331 start = is_int32(d) ? min(length, d) : length;
1334 if(argc >= 2) {
1335 hres = to_integer(ctx, argv[1], &d);
1336 if(FAILED(hres)) {
1337 jsstr_release(str);
1338 return hres;
1341 if(d >= 0.0)
1342 len = is_int32(d) ? min(length-start, d) : length-start;
1343 else
1344 len = 0;
1345 }else {
1346 len = length-start;
1349 hres = S_OK;
1350 if(r) {
1351 jsstr_t *ret = jsstr_substr(str, start, len);
1352 if(ret)
1353 *r = jsval_string(ret);
1354 else
1355 hres = E_OUTOFMEMORY;
1358 jsstr_release(str);
1359 return hres;
1362 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1363 jsval_t *r)
1365 return do_attributeless_tag_format(ctx, jsthis, r, L"SUP");
1368 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1369 jsval_t *r)
1371 jsstr_t *str;
1372 HRESULT hres;
1374 TRACE("\n");
1376 hres = get_string_val(ctx, jsthis, &str);
1377 if(FAILED(hres))
1378 return hres;
1380 if(r) {
1381 unsigned len = jsstr_length(str);
1382 jsstr_t *ret;
1383 WCHAR *buf;
1385 ret = jsstr_alloc_buf(len, &buf);
1386 if(!ret) {
1387 jsstr_release(str);
1388 return E_OUTOFMEMORY;
1391 jsstr_flush(str, buf);
1392 for (; len--; buf++) *buf = towlower(*buf);
1394 *r = jsval_string(ret);
1396 jsstr_release(str);
1397 return S_OK;
1400 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1401 jsval_t *r)
1403 jsstr_t *str;
1404 HRESULT hres;
1406 TRACE("\n");
1408 hres = get_string_val(ctx, jsthis, &str);
1409 if(FAILED(hres))
1410 return hres;
1412 if(r) {
1413 unsigned len = jsstr_length(str);
1414 jsstr_t *ret;
1415 WCHAR *buf;
1417 ret = jsstr_alloc_buf(len, &buf);
1418 if(!ret) {
1419 jsstr_release(str);
1420 return E_OUTOFMEMORY;
1423 jsstr_flush(str, buf);
1424 for (; len--; buf++) *buf = towupper(*buf);
1426 *r = jsval_string(ret);
1428 jsstr_release(str);
1429 return S_OK;
1432 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1433 jsval_t *r)
1435 FIXME("\n");
1436 return E_NOTIMPL;
1439 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1440 jsval_t *r)
1442 FIXME("\n");
1443 return E_NOTIMPL;
1446 static HRESULT String_trim(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc,
1447 jsval_t *argv, jsval_t *r)
1449 const WCHAR *str, *begin, *end;
1450 jsstr_t *jsstr;
1451 unsigned len;
1452 HRESULT hres;
1454 hres = to_flat_string(ctx, jsval_disp(jsthis->u.disp), &jsstr, &str);
1455 if(FAILED(hres)) {
1456 WARN("to_flat_string failed: %08x\n", hres);
1457 return hres;
1459 len = jsstr_length(jsstr);
1460 TRACE("%s\n", debugstr_wn(str, len));
1462 for(begin = str, end = str + len; begin < end && iswspace(*begin); begin++);
1463 while(end > begin + 1 && iswspace(*(end-1))) end--;
1465 if(r) {
1466 jsstr_t *ret;
1468 if(begin == str && end == str + len)
1469 ret = jsstr_addref(jsstr);
1470 else
1471 ret = jsstr_alloc_len(begin, end - begin);
1472 if(ret)
1473 *r = jsval_string(ret);
1474 else
1475 hres = E_OUTOFMEMORY;
1477 jsstr_release(jsstr);
1478 return hres;
1481 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1482 jsval_t *r)
1484 FIXME("\n");
1485 return E_NOTIMPL;
1488 static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
1490 StringInstance *This = string_from_jsdisp(jsthis);
1492 TRACE("\n");
1494 *r = jsval_string(jsstr_addref(This->str));
1495 return S_OK;
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, NULL,0, String_get_value},
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, NULL,0, String_get_value},
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, vdisp_t *jsthis, WORD flags,
1598 unsigned argc, jsval_t *argv, jsval_t *r)
1600 WCHAR *ret_str;
1601 DWORD 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, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1629 jsval_t *r)
1631 HRESULT hres;
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 *r = jsval_string(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 hres = create_string(ctx, str, &ret);
1663 if (SUCCEEDED(hres)) *r = jsval_obj(ret);
1664 jsstr_release(str);
1665 return hres;
1668 default:
1669 FIXME("unimplemented flags: %x\n", flags);
1670 return E_NOTIMPL;
1673 return S_OK;
1676 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1678 StringInstance *string;
1679 HRESULT hres;
1681 string = heap_alloc_zero(sizeof(StringInstance));
1682 if(!string)
1683 return E_OUTOFMEMORY;
1685 if(object_prototype)
1686 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1687 else
1688 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1689 if(FAILED(hres)) {
1690 heap_free(string);
1691 return hres;
1694 string->str = jsstr_addref(str);
1695 *ret = string;
1696 return S_OK;
1699 static const builtin_prop_t StringConstr_props[] = {
1700 {L"fromCharCode", StringConstr_fromCharCode, PROPF_METHOD},
1703 static const builtin_info_t StringConstr_info = {
1704 JSCLASS_FUNCTION,
1705 DEFAULT_FUNCTION_VALUE,
1706 ARRAY_SIZE(StringConstr_props),
1707 StringConstr_props,
1708 NULL,
1709 NULL
1712 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1714 StringInstance *string;
1715 HRESULT hres;
1717 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1718 if(FAILED(hres))
1719 return hres;
1721 hres = create_builtin_constructor(ctx, StringConstr_value, L"String", &StringConstr_info,
1722 PROPF_CONSTR|1, &string->dispex, ret);
1724 jsdisp_release(&string->dispex);
1725 return hres;
1728 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1730 StringInstance *string;
1731 HRESULT hres;
1733 hres = string_alloc(ctx, NULL, str, &string);
1734 if(FAILED(hres))
1735 return hres;
1737 *ret = &string->dispex;
1738 return S_OK;