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
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
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
);
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
)
64 hres
= get_string_val(ctx
, vthis
, jsval
);
68 *val
= jsstr_flatten(*jsval
);
72 jsstr_release(*jsval
);
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
));
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");
96 *r
= jsval_string(jsstr_addref(string
->str
));
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
,
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
,
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
;
125 hres
= get_string_val(ctx
, vthis
, &str
);
134 tagname_len
= lstrlenW(tagname
);
136 ret
= jsstr_alloc_buf(jsstr_length(str
) + 2*tagname_len
+ 5, &ptr
);
139 return E_OUTOFMEMORY
;
143 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
147 ptr
+= jsstr_flush(str
, ptr
);
152 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
156 *r
= jsval_string(ret
);
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
;
166 hres
= get_string_val(ctx
, vthis
, &str
);
171 hres
= to_string(ctx
, argv
[0], &attr_value
);
177 attr_value
= jsstr_undefined();
181 unsigned attrname_len
= lstrlenW(attrname
);
182 unsigned tagname_len
= lstrlenW(tagname
);
186 ret
= jsstr_alloc_buf(2*tagname_len
+ attrname_len
+ jsstr_length(attr_value
) + jsstr_length(str
) + 9, &ptr
);
189 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
192 memcpy(ptr
, attrname
, attrname_len
*sizeof(WCHAR
));
196 ptr
+= jsstr_flush(attr_value
, ptr
);
199 ptr
+= jsstr_flush(str
, ptr
);
203 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
207 *r
= jsval_string(ret
);
209 hres
= E_OUTOFMEMORY
;
213 jsstr_release(attr_value
);
218 static HRESULT
String_anchor(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
,
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
,
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
,
252 hres
= get_string_val(ctx
, vthis
, &str
);
259 hres
= to_integer(ctx
, argv
[0], &d
);
264 pos
= is_int32(d
) ? d
: -1;
272 if(0 <= pos
&& pos
< jsstr_length(str
)) {
273 ret
= jsstr_substr(str
, pos
, 1);
275 return E_OUTOFMEMORY
;
280 *r
= jsval_string(ret
);
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
,
294 hres
= get_string_val(ctx
, vthis
, &str
);
301 hres
= to_integer(ctx
, argv
[0], &d
);
307 if(!is_int32(d
) || d
< 0 || d
>= jsstr_length(str
)) {
310 *r
= jsval_number(NAN
);
319 jsstr_extract(str
, idx
, 1, &c
);
320 *r
= jsval_number(c
);
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
,
331 jsstr_t
*ret
= NULL
, *str
;
336 hres
= get_string_val(ctx
, vthis
, &str
);
347 hres
= to_string(ctx
, argv
[0], &arg_str
);
353 ret
= jsstr_concat(str
, arg_str
);
356 return E_OUTOFMEMORY
;
360 const unsigned str_cnt
= argc
+1;
365 strs
= calloc(str_cnt
, sizeof(*strs
));
368 return E_OUTOFMEMORY
;
372 for(i
=0; i
< argc
; i
++) {
373 hres
= to_string(ctx
, argv
[i
], strs
+i
+1);
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
;
387 if(SUCCEEDED(hres
)) {
388 ret
= jsstr_alloc_buf(len
, &ptr
);
390 for(i
=0; i
< str_cnt
; i
++)
391 ptr
+= jsstr_flush(strs
[i
], ptr
);
393 hres
= E_OUTOFMEMORY
;
399 jsstr_release(strs
[i
]);
407 *r
= jsval_string(ret
);
413 static HRESULT
String_fixed(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
,
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
,
434 unsigned pos
= 0, search_len
, length
;
435 jsstr_t
*search_jsstr
, *jsstr
;
436 const WCHAR
*search_str
, *str
;
442 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
448 *r
= jsval_number(-1);
449 jsstr_release(jsstr
);
453 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
455 jsstr_release(jsstr
);
459 search_len
= jsstr_length(search_jsstr
);
460 length
= jsstr_length(jsstr
);
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
;
474 for(ptr
= str
+pos
; ptr
<= end
; ptr
++) {
475 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
482 jsstr_release(search_jsstr
);
483 jsstr_release(jsstr
);
488 *r
= jsval_number(ret
);
492 static HRESULT
String_italics(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
502 unsigned pos
= 0, search_len
, length
;
503 jsstr_t
*search_jsstr
, *jsstr
;
504 const WCHAR
*search_str
, *str
;
510 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
516 *r
= jsval_number(-1);
517 jsstr_release(jsstr
);
521 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
523 jsstr_release(jsstr
);
527 search_len
= jsstr_length(search_jsstr
);
528 length
= jsstr_length(jsstr
);
533 hres
= to_integer(ctx
, argv
[1], &d
);
534 if(SUCCEEDED(hres
) && d
> 0)
535 pos
= is_int32(d
) ? min(length
, d
) : length
;
540 if(SUCCEEDED(hres
) && length
>= search_len
) {
543 for(ptr
= str
+min(pos
, length
-search_len
); ptr
>= str
; ptr
--) {
544 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
551 jsstr_release(search_jsstr
);
552 jsstr_release(jsstr
);
557 *r
= jsval_number(ret
);
561 static HRESULT
String_link(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
571 jsdisp_t
*regexp
= NULL
;
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
);
594 hres
= to_string(ctx
, argv
[0], &match_str
);
598 hres
= create_regexp(ctx
, match_str
, 0, ®exp
);
599 jsstr_release(match_str
);
604 hres
= get_string_val(ctx
, vthis
, &str
);
606 hres
= regexp_string_match(ctx
, regexp
, str
, r
);
608 jsdisp_release(regexp
);
619 static BOOL
strbuf_ensure_size(strbuf_t
*buf
, unsigned len
)
627 new_size
= buf
->size
? buf
->size
<<1 : 16;
631 new_buf
= realloc(buf
->buf
, new_size
*sizeof(WCHAR
));
633 new_buf
= malloc(new_size
*sizeof(WCHAR
));
638 buf
->size
= new_size
;
642 static HRESULT
strbuf_append(strbuf_t
*buf
, const WCHAR
*str
, DWORD len
)
647 if(!strbuf_ensure_size(buf
, buf
->len
+len
))
648 return E_OUTOFMEMORY
;
650 memcpy(buf
->buf
+buf
->len
, str
, len
*sizeof(WCHAR
));
655 static HRESULT
strbuf_append_jsstr(strbuf_t
*buf
, jsstr_t
*str
)
657 if(!strbuf_ensure_size(buf
, buf
->len
+jsstr_length(str
)))
658 return E_OUTOFMEMORY
;
660 jsstr_flush(str
, buf
->buf
+buf
->len
);
661 buf
->len
+= jsstr_length(str
);
665 static HRESULT
rep_call(script_ctx_t
*ctx
, jsdisp_t
*func
,
666 jsstr_t
*jsstr
, const WCHAR
*str
, match_state_t
*match
, jsstr_t
**ret
)
675 argc
= match
->paren_count
+3;
676 argv
= calloc(argc
, sizeof(*argv
));
678 return E_OUTOFMEMORY
;
680 tmp_str
= jsstr_alloc_len(match
->cp
-match
->match_len
, match
->match_len
);
682 hres
= E_OUTOFMEMORY
;
683 argv
[0] = jsval_string(tmp_str
);
685 if(SUCCEEDED(hres
)) {
686 for(i
=0; i
< match
->paren_count
; i
++) {
687 if(match
->parens
[i
].index
!= -1)
688 tmp_str
= jsstr_substr(jsstr
, match
->parens
[i
].index
, match
->parens
[i
].length
);
690 tmp_str
= jsstr_empty();
692 hres
= E_OUTOFMEMORY
;
695 argv
[i
+1] = jsval_string(tmp_str
);
699 if(SUCCEEDED(hres
)) {
700 argv
[match
->paren_count
+1] = jsval_number(match
->cp
-str
- match
->match_len
);
701 argv
[match
->paren_count
+2] = jsval_string(jsstr
);
705 hres
= jsdisp_call_value(func
, jsval_undefined(), DISPATCH_METHOD
, argc
, argv
, &val
);
707 for(i
=0; i
<= match
->paren_count
; i
++)
708 jsstr_release(get_string(argv
[i
]));
714 hres
= to_string(ctx
, val
, ret
);
719 /* ECMA-262 3rd Edition 15.5.4.11 */
720 static HRESULT
String_replace(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
723 const WCHAR
*str
, *match_str
= NULL
, *rep_str
= NULL
;
724 jsstr_t
*rep_jsstr
, *match_jsstr
, *jsstr
;
725 jsdisp_t
*rep_func
= NULL
, *regexp
= NULL
;
726 match_state_t
*match
= NULL
, last_match
= {0};
727 strbuf_t ret
= {NULL
,0,0};
728 DWORD re_flags
= REM_NO_CTX_UPDATE
|REM_ALLOC_RESULT
;
734 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
740 *r
= jsval_string(jsstr
);
742 jsstr_release(jsstr
);
746 if(is_object_instance(argv
[0])) {
747 regexp
= iface_to_jsdisp(get_object(argv
[0]));
748 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
749 jsdisp_release(regexp
);
755 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
757 jsstr_release(jsstr
);
763 if(is_object_instance(argv
[1])) {
764 rep_func
= iface_to_jsdisp(get_object(argv
[1]));
765 if(rep_func
&& !is_class(rep_func
, JSCLASS_FUNCTION
)) {
766 jsdisp_release(rep_func
);
772 hres
= to_flat_string(ctx
, argv
[1], &rep_jsstr
, &rep_str
);
774 rep_len
= jsstr_length(rep_jsstr
);
778 if(SUCCEEDED(hres
)) {
779 const WCHAR
*ecp
= str
;
783 hres
= regexp_match_next(ctx
, regexp
, re_flags
, jsstr
, &match
);
784 re_flags
= (re_flags
| REM_CHECK_GLOBAL
) & (~REM_ALLOC_RESULT
);
786 if(hres
== S_FALSE
) {
793 last_match
.cp
= match
->cp
;
794 last_match
.match_len
= match
->match_len
;
796 if(re_flags
& REM_ALLOC_RESULT
) {
797 re_flags
&= ~REM_ALLOC_RESULT
;
802 match
->cp
= wcsstr(match
->cp
, match_str
);
805 match
->match_len
= jsstr_length(match_jsstr
);
806 match
->cp
+= match
->match_len
;
809 hres
= strbuf_append(&ret
, ecp
, match
->cp
-ecp
-match
->match_len
);
817 hres
= rep_call(ctx
, rep_func
, jsstr
, str
, match
, &cstr
);
821 hres
= strbuf_append_jsstr(&ret
, cstr
);
825 }else if(rep_str
&& regexp
) {
826 const WCHAR
*ptr
= rep_str
, *ptr2
;
828 while((ptr2
= wcschr(ptr
, '$'))) {
829 hres
= strbuf_append(&ret
, ptr
, ptr2
-ptr
);
835 hres
= strbuf_append(&ret
, ptr2
, 1);
839 hres
= strbuf_append(&ret
, match
->cp
-match
->match_len
, match
->match_len
);
843 hres
= strbuf_append(&ret
, str
, match
->cp
-str
-match
->match_len
);
847 hres
= strbuf_append(&ret
, ecp
, (str
+jsstr_length(jsstr
))-ecp
);
853 if(!is_digit(ptr2
[1])) {
854 hres
= strbuf_append(&ret
, ptr2
, 1);
860 if(is_digit(ptr2
[2]) && idx
*10 + (ptr2
[2]-'0') <= match
->paren_count
) {
861 idx
= idx
*10 + (ptr
[2]-'0');
863 }else if(idx
&& idx
<= match
->paren_count
) {
866 hres
= strbuf_append(&ret
, ptr2
, 1);
871 if(match
->parens
[idx
-1].index
!= -1)
872 hres
= strbuf_append(&ret
, str
+match
->parens
[idx
-1].index
,
873 match
->parens
[idx
-1].length
);
882 hres
= strbuf_append(&ret
, ptr
, (rep_str
+rep_len
)-ptr
);
886 hres
= strbuf_append(&ret
, rep_str
, rep_len
);
890 hres
= strbuf_append(&ret
, L
"undefined", ARRAY_SIZE(L
"undefined")-1);
897 else if(!match
->match_len
)
902 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
906 jsdisp_release(rep_func
);
908 jsstr_release(rep_jsstr
);
910 jsstr_release(match_jsstr
);
914 if(SUCCEEDED(hres
) && last_match
.cp
&& regexp
) {
915 jsstr_release(ctx
->last_match
);
916 ctx
->last_match
= jsstr_addref(jsstr
);
917 ctx
->last_match_index
= last_match
.cp
-str
-last_match
.match_len
;
918 ctx
->last_match_length
= last_match
.match_len
;
922 jsdisp_release(regexp
);
923 jsstr_release(jsstr
);
925 if(SUCCEEDED(hres
) && r
) {
928 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
930 return E_OUTOFMEMORY
;
932 TRACE("= %s\n", debugstr_jsstr(ret_str
));
933 *r
= jsval_string(ret_str
);
940 static HRESULT
String_search(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
943 jsdisp_t
*regexp
= NULL
;
946 match_state_t match
, *match_ptr
= &match
;
951 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
958 jsstr_release(jsstr
);
962 if(is_object_instance(argv
[0])) {
963 regexp
= iface_to_jsdisp(get_object(argv
[0]));
964 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
965 jsdisp_release(regexp
);
971 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
973 jsstr_release(jsstr
);
979 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
980 jsstr_release(jsstr
);
981 jsdisp_release(regexp
);
986 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
990 /* ECMA-262 3rd Edition 15.5.4.13 */
991 static HRESULT
String_slice(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
994 int start
=0, end
, length
;
1001 hres
= get_string_val(ctx
, vthis
, &str
);
1005 length
= jsstr_length(str
);
1007 hres
= to_integer(ctx
, argv
[0], &d
);
1016 start
= length
+ start
;
1019 }else if(start
> length
) {
1028 hres
= to_integer(ctx
, argv
[1], &d
);
1040 }else if(end
> length
) {
1044 end
= d
< 0.0 ? 0 : length
;
1054 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1057 return E_OUTOFMEMORY
;
1060 *r
= jsval_string(retstr
);
1067 static HRESULT
String_small(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1070 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SMALL");
1073 static HRESULT
String_split(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1076 match_state_t match_result
, *match_ptr
= &match_result
;
1077 size_t length
, i
= 0, match_len
= 0;
1078 const WCHAR
*ptr
, *ptr2
, *str
, *match_str
= NULL
;
1079 unsigned limit
= ~0u;
1080 jsdisp_t
*array
, *regexp
= NULL
;
1081 jsstr_t
*jsstr
, *match_jsstr
, *tmp_str
;
1084 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
1087 length
= jsstr_length(jsstr
);
1089 TRACE("%s\n", debugstr_wn(str
, length
));
1091 if(!argc
|| (is_undefined(argv
[0]) && ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
)) {
1095 hres
= create_array(ctx
, 0, &array
);
1099 /* NOTE: according to spec, we should respect limit argument here (if provided).
1100 * We have a test showing that it's broken in native IE. */
1101 hres
= jsdisp_propput_idx(array
, 0, jsval_string(jsstr
));
1103 jsdisp_release(array
);
1107 *r
= jsval_obj(array
);
1111 if(argc
> 1 && !is_undefined(argv
[1])) {
1112 hres
= to_uint32(ctx
, argv
[1], &limit
);
1114 jsstr_release(jsstr
);
1119 if(is_object_instance(argv
[0])) {
1120 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1122 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1123 jsdisp_release(regexp
);
1130 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1132 jsstr_release(jsstr
);
1136 match_len
= jsstr_length(match_jsstr
);
1138 jsstr_release(match_jsstr
);
1143 hres
= create_array(ctx
, 0, &array
);
1145 if(SUCCEEDED(hres
)) {
1147 match_result
.cp
= str
;
1150 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1153 TRACE("got match %d %ld\n", (int)(match_result
.cp
- match_result
.match_len
- str
), match_result
.match_len
);
1154 if(!match_result
.match_len
) {
1155 /* If an empty string is matched, prevent including any match in the result */
1160 if(match_result
.cp
== ptr
) {
1162 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1165 TRACE("retried, got match %d %ld\n", (int)(match_result
.cp
- match_result
.match_len
- str
),
1166 match_result
.match_len
);
1168 if(!match_result
.match_len
&& match_result
.cp
== str
+ length
)
1171 ptr2
= match_result
.cp
- match_result
.match_len
;
1172 }else if(match_str
) {
1173 ptr2
= wcsstr(ptr
, match_str
);
1182 if(!regexp
|| ptr2
> ptr
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1183 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1185 hres
= E_OUTOFMEMORY
;
1189 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(tmp_str
));
1190 jsstr_release(tmp_str
);
1196 ptr
= match_result
.cp
;
1198 ptr
= ptr2
+ match_len
;
1204 if(SUCCEEDED(hres
) && (match_str
|| regexp
) && i
<limit
) {
1205 DWORD len
= (str
+length
) - ptr
;
1207 if(len
|| match_str
|| !length
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1208 tmp_str
= jsstr_alloc_len(ptr
, len
);
1211 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1212 jsstr_release(tmp_str
);
1214 hres
= E_OUTOFMEMORY
;
1220 jsdisp_release(regexp
);
1222 jsstr_release(match_jsstr
);
1223 jsstr_release(jsstr
);
1225 if(SUCCEEDED(hres
) && r
)
1226 *r
= jsval_obj(array
);
1228 jsdisp_release(array
);
1233 static HRESULT
String_strike(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1236 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"STRIKE");
1239 static HRESULT
String_sub(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1242 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SUB");
1245 /* ECMA-262 3rd Edition 15.5.4.15 */
1246 static HRESULT
String_substring(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1249 INT start
=0, end
, length
;
1256 hres
= get_string_val(ctx
, vthis
, &str
);
1260 length
= jsstr_length(str
);
1262 hres
= to_integer(ctx
, argv
[0], &d
);
1269 start
= is_int32(d
) ? min(length
, d
) : length
;
1273 hres
= to_integer(ctx
, argv
[1], &d
);
1280 end
= is_int32(d
) ? min(length
, d
) : length
;
1294 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1296 *r
= jsval_string(ret
);
1298 hres
= E_OUTOFMEMORY
;
1304 /* ECMA-262 3rd Edition B.2.3 */
1305 static HRESULT
String_substr(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1308 int start
=0, len
, length
;
1315 hres
= get_string_val(ctx
, vthis
, &str
);
1319 length
= jsstr_length(str
);
1321 hres
= to_integer(ctx
, argv
[0], &d
);
1328 start
= is_int32(d
) ? min(length
, d
) : length
;
1332 hres
= to_integer(ctx
, argv
[1], &d
);
1339 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1348 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1350 *r
= jsval_string(ret
);
1352 hres
= E_OUTOFMEMORY
;
1359 static HRESULT
String_sup(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1362 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SUP");
1365 static HRESULT
to_upper_case(script_ctx_t
*ctx
, jsval_t vthis
, jsval_t
*r
)
1371 hres
= get_string_val(ctx
, vthis
, &str
);
1376 unsigned len
= jsstr_length(str
);
1380 ret
= jsstr_alloc_buf(len
, &buf
);
1383 return E_OUTOFMEMORY
;
1386 jsstr_flush(str
, buf
);
1387 for (; len
--; buf
++) *buf
= towupper(*buf
);
1389 *r
= jsval_string(ret
);
1395 static HRESULT
to_lower_case(script_ctx_t
*ctx
, jsval_t vthis
, jsval_t
*r
)
1401 hres
= get_string_val(ctx
, vthis
, &str
);
1406 unsigned len
= jsstr_length(str
);
1410 ret
= jsstr_alloc_buf(len
, &buf
);
1413 return E_OUTOFMEMORY
;
1416 jsstr_flush(str
, buf
);
1417 for (; len
--; buf
++) *buf
= towlower(*buf
);
1419 *r
= jsval_string(ret
);
1425 static HRESULT
String_toLowerCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1429 return to_lower_case(ctx
, vthis
, r
);
1432 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1436 return to_upper_case(ctx
, vthis
, r
);
1439 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1443 return to_lower_case(ctx
, vthis
, r
);
1446 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1450 return to_upper_case(ctx
, vthis
, r
);
1453 static HRESULT
String_trim(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
,
1454 jsval_t
*argv
, jsval_t
*r
)
1456 const WCHAR
*str
, *begin
, *end
;
1461 if(is_undefined(vthis
) || is_null(vthis
))
1462 return JS_E_OBJECT_EXPECTED
;
1464 hres
= to_flat_string(ctx
, vthis
, &jsstr
, &str
);
1466 WARN("to_flat_string failed: %08lx\n", hres
);
1469 len
= jsstr_length(jsstr
);
1470 TRACE("%s\n", debugstr_wn(str
, len
));
1472 for(begin
= str
, end
= str
+ len
; begin
< end
&& iswspace(*begin
); begin
++);
1473 while(end
> begin
+ 1 && iswspace(*(end
-1))) end
--;
1478 if(begin
== str
&& end
== str
+ len
)
1479 ret
= jsstr_addref(jsstr
);
1481 ret
= jsstr_alloc_len(begin
, end
- begin
);
1483 *r
= jsval_string(ret
);
1485 hres
= E_OUTOFMEMORY
;
1487 jsstr_release(jsstr
);
1491 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1498 static void String_destructor(jsdisp_t
*dispex
)
1500 StringInstance
*This
= string_from_jsdisp(dispex
);
1502 jsstr_release(This
->str
);
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
);
1525 ret
= jsstr_substr(string
->str
, idx
, 1);
1527 return E_OUTOFMEMORY
;
1529 TRACE("%p[%u] = %s\n", string
, idx
, debugstr_jsstr(ret
));
1531 *r
= jsval_string(ret
);
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
= {
1575 ARRAY_SIZE(String_props
),
1581 static const builtin_prop_t StringInst_props
[] = {
1582 {L
"length", NULL
,0, String_get_length
}
1585 static const builtin_info_t StringInst_info
= {
1588 ARRAY_SIZE(StringInst_props
),
1596 /* ECMA-262 3rd Edition 15.5.3.2 */
1597 static HRESULT
StringConstr_fromCharCode(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
,
1598 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1607 ret
= jsstr_alloc_buf(argc
, &ret_str
);
1609 return E_OUTOFMEMORY
;
1611 for(i
=0; i
<argc
; i
++) {
1612 hres
= to_uint32(ctx
, argv
[i
], &code
);
1622 *r
= jsval_string(ret
);
1628 static HRESULT
StringConstr_value(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1631 HRESULT hres
= S_OK
;
1640 hres
= to_string(ctx
, argv
[0], &str
);
1644 str
= jsstr_empty();
1647 if(r
) *r
= jsval_string(str
);
1648 else jsstr_release(str
);
1651 case DISPATCH_CONSTRUCT
: {
1656 hres
= to_string(ctx
, argv
[0], &str
);
1660 str
= jsstr_empty();
1664 hres
= create_string(ctx
, str
, &ret
);
1665 if(SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1672 FIXME("unimplemented flags: %x\n", flags
);
1679 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1681 StringInstance
*string
;
1684 string
= calloc(1, sizeof(StringInstance
));
1686 return E_OUTOFMEMORY
;
1688 if(object_prototype
)
1689 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1691 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1697 string
->str
= jsstr_addref(str
);
1702 static const builtin_prop_t StringConstr_props
[] = {
1703 {L
"fromCharCode", StringConstr_fromCharCode
, PROPF_METHOD
},
1706 static const builtin_info_t StringConstr_info
= {
1709 ARRAY_SIZE(StringConstr_props
),
1715 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1717 StringInstance
*string
;
1720 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1724 hres
= create_builtin_constructor(ctx
, StringConstr_value
, L
"String", &StringConstr_info
,
1725 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1727 jsdisp_release(&string
->dispex
);
1731 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1733 StringInstance
*string
;
1736 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1740 *ret
= &string
->dispex
;