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;
630 new_buf
= realloc(buf
->buf
, new_size
* sizeof(WCHAR
));
635 buf
->size
= new_size
;
639 static HRESULT
strbuf_append(strbuf_t
*buf
, const WCHAR
*str
, DWORD len
)
644 if(!strbuf_ensure_size(buf
, buf
->len
+len
))
645 return E_OUTOFMEMORY
;
647 memcpy(buf
->buf
+buf
->len
, str
, len
*sizeof(WCHAR
));
652 static HRESULT
strbuf_append_jsstr(strbuf_t
*buf
, jsstr_t
*str
)
654 if(!strbuf_ensure_size(buf
, buf
->len
+jsstr_length(str
)))
655 return E_OUTOFMEMORY
;
657 jsstr_flush(str
, buf
->buf
+buf
->len
);
658 buf
->len
+= jsstr_length(str
);
662 static HRESULT
rep_call(script_ctx_t
*ctx
, jsdisp_t
*func
,
663 jsstr_t
*jsstr
, const WCHAR
*str
, match_state_t
*match
, jsstr_t
**ret
)
672 argc
= match
->paren_count
+3;
673 argv
= calloc(argc
, sizeof(*argv
));
675 return E_OUTOFMEMORY
;
677 tmp_str
= jsstr_alloc_len(match
->cp
-match
->match_len
, match
->match_len
);
679 hres
= E_OUTOFMEMORY
;
680 argv
[0] = jsval_string(tmp_str
);
682 if(SUCCEEDED(hres
)) {
683 for(i
=0; i
< match
->paren_count
; i
++) {
684 if(match
->parens
[i
].index
!= -1)
685 tmp_str
= jsstr_substr(jsstr
, match
->parens
[i
].index
, match
->parens
[i
].length
);
687 tmp_str
= jsstr_empty();
689 hres
= E_OUTOFMEMORY
;
692 argv
[i
+1] = jsval_string(tmp_str
);
696 if(SUCCEEDED(hres
)) {
697 argv
[match
->paren_count
+1] = jsval_number(match
->cp
-str
- match
->match_len
);
698 argv
[match
->paren_count
+2] = jsval_string(jsstr
);
702 hres
= jsdisp_call_value(func
, jsval_undefined(), DISPATCH_METHOD
, argc
, argv
, &val
);
704 for(i
=0; i
<= match
->paren_count
; i
++)
705 jsstr_release(get_string(argv
[i
]));
711 hres
= to_string(ctx
, val
, ret
);
716 /* ECMA-262 3rd Edition 15.5.4.11 */
717 static HRESULT
String_replace(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
720 const WCHAR
*str
, *match_str
= NULL
, *rep_str
= NULL
;
721 jsstr_t
*rep_jsstr
, *match_jsstr
, *jsstr
;
722 jsdisp_t
*rep_func
= NULL
, *regexp
= NULL
;
723 match_state_t
*match
= NULL
, last_match
= {0};
724 strbuf_t ret
= {NULL
,0,0};
725 DWORD re_flags
= REM_NO_CTX_UPDATE
|REM_ALLOC_RESULT
;
731 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
737 *r
= jsval_string(jsstr
);
739 jsstr_release(jsstr
);
743 if(is_object_instance(argv
[0])) {
744 regexp
= iface_to_jsdisp(get_object(argv
[0]));
745 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
746 jsdisp_release(regexp
);
752 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
754 jsstr_release(jsstr
);
760 if(is_object_instance(argv
[1])) {
761 rep_func
= iface_to_jsdisp(get_object(argv
[1]));
762 if(rep_func
&& !is_class(rep_func
, JSCLASS_FUNCTION
)) {
763 jsdisp_release(rep_func
);
769 hres
= to_flat_string(ctx
, argv
[1], &rep_jsstr
, &rep_str
);
771 rep_len
= jsstr_length(rep_jsstr
);
775 if(SUCCEEDED(hres
)) {
776 const WCHAR
*ecp
= str
;
780 hres
= regexp_match_next(ctx
, regexp
, re_flags
, jsstr
, &match
);
781 re_flags
= (re_flags
| REM_CHECK_GLOBAL
) & (~REM_ALLOC_RESULT
);
783 if(hres
== S_FALSE
) {
790 last_match
.cp
= match
->cp
;
791 last_match
.match_len
= match
->match_len
;
793 if(re_flags
& REM_ALLOC_RESULT
) {
794 re_flags
&= ~REM_ALLOC_RESULT
;
799 match
->cp
= wcsstr(match
->cp
, match_str
);
802 match
->match_len
= jsstr_length(match_jsstr
);
803 match
->cp
+= match
->match_len
;
806 hres
= strbuf_append(&ret
, ecp
, match
->cp
-ecp
-match
->match_len
);
814 hres
= rep_call(ctx
, rep_func
, jsstr
, str
, match
, &cstr
);
818 hres
= strbuf_append_jsstr(&ret
, cstr
);
822 }else if(rep_str
&& regexp
) {
823 const WCHAR
*ptr
= rep_str
, *ptr2
;
825 while((ptr2
= wcschr(ptr
, '$'))) {
826 hres
= strbuf_append(&ret
, ptr
, ptr2
-ptr
);
832 hres
= strbuf_append(&ret
, ptr2
, 1);
836 hres
= strbuf_append(&ret
, match
->cp
-match
->match_len
, match
->match_len
);
840 hres
= strbuf_append(&ret
, str
, match
->cp
-str
-match
->match_len
);
844 hres
= strbuf_append(&ret
, ecp
, (str
+jsstr_length(jsstr
))-ecp
);
850 if(!is_digit(ptr2
[1])) {
851 hres
= strbuf_append(&ret
, ptr2
, 1);
857 if(is_digit(ptr2
[2]) && idx
*10 + (ptr2
[2]-'0') <= match
->paren_count
) {
858 idx
= idx
*10 + (ptr
[2]-'0');
860 }else if(idx
&& idx
<= match
->paren_count
) {
863 hres
= strbuf_append(&ret
, ptr2
, 1);
868 if(match
->parens
[idx
-1].index
!= -1)
869 hres
= strbuf_append(&ret
, str
+match
->parens
[idx
-1].index
,
870 match
->parens
[idx
-1].length
);
879 hres
= strbuf_append(&ret
, ptr
, (rep_str
+rep_len
)-ptr
);
883 hres
= strbuf_append(&ret
, rep_str
, rep_len
);
887 hres
= strbuf_append(&ret
, L
"undefined", ARRAY_SIZE(L
"undefined")-1);
894 else if(!match
->match_len
)
899 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
903 jsdisp_release(rep_func
);
905 jsstr_release(rep_jsstr
);
907 jsstr_release(match_jsstr
);
911 if(SUCCEEDED(hres
) && last_match
.cp
&& regexp
) {
912 jsstr_release(ctx
->last_match
);
913 ctx
->last_match
= jsstr_addref(jsstr
);
914 ctx
->last_match_index
= last_match
.cp
-str
-last_match
.match_len
;
915 ctx
->last_match_length
= last_match
.match_len
;
919 jsdisp_release(regexp
);
920 jsstr_release(jsstr
);
922 if(SUCCEEDED(hres
) && r
) {
925 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
928 return E_OUTOFMEMORY
;
931 TRACE("= %s\n", debugstr_jsstr(ret_str
));
932 *r
= jsval_string(ret_str
);
939 static HRESULT
String_search(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
942 jsdisp_t
*regexp
= NULL
;
945 match_state_t match
, *match_ptr
= &match
;
950 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
957 jsstr_release(jsstr
);
961 if(is_object_instance(argv
[0])) {
962 regexp
= iface_to_jsdisp(get_object(argv
[0]));
963 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
964 jsdisp_release(regexp
);
970 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
972 jsstr_release(jsstr
);
978 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
979 jsstr_release(jsstr
);
980 jsdisp_release(regexp
);
985 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
989 /* ECMA-262 3rd Edition 15.5.4.13 */
990 static HRESULT
String_slice(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
993 int start
=0, end
, length
;
1000 hres
= get_string_val(ctx
, vthis
, &str
);
1004 length
= jsstr_length(str
);
1006 hres
= to_integer(ctx
, argv
[0], &d
);
1015 start
= length
+ start
;
1018 }else if(start
> length
) {
1027 hres
= to_integer(ctx
, argv
[1], &d
);
1039 }else if(end
> length
) {
1043 end
= d
< 0.0 ? 0 : length
;
1053 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1056 return E_OUTOFMEMORY
;
1059 *r
= jsval_string(retstr
);
1066 static HRESULT
String_small(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1069 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SMALL");
1072 static HRESULT
String_split(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1075 match_state_t match_result
, *match_ptr
= &match_result
;
1076 size_t length
, i
= 0, match_len
= 0;
1077 const WCHAR
*ptr
, *ptr2
, *str
, *match_str
= NULL
;
1078 unsigned limit
= ~0u;
1079 jsdisp_t
*array
, *regexp
= NULL
;
1080 jsstr_t
*jsstr
, *match_jsstr
, *tmp_str
;
1083 hres
= get_string_flat_val(ctx
, vthis
, &jsstr
, &str
);
1086 length
= jsstr_length(jsstr
);
1088 TRACE("%s\n", debugstr_wn(str
, length
));
1090 if(!argc
|| (is_undefined(argv
[0]) && ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
)) {
1094 hres
= create_array(ctx
, 0, &array
);
1098 /* NOTE: according to spec, we should respect limit argument here (if provided).
1099 * We have a test showing that it's broken in native IE. */
1100 hres
= jsdisp_propput_idx(array
, 0, jsval_string(jsstr
));
1102 jsdisp_release(array
);
1106 *r
= jsval_obj(array
);
1110 if(argc
> 1 && !is_undefined(argv
[1])) {
1111 hres
= to_uint32(ctx
, argv
[1], &limit
);
1113 jsstr_release(jsstr
);
1118 if(is_object_instance(argv
[0])) {
1119 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1121 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1122 jsdisp_release(regexp
);
1129 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1131 jsstr_release(jsstr
);
1135 match_len
= jsstr_length(match_jsstr
);
1137 jsstr_release(match_jsstr
);
1142 hres
= create_array(ctx
, 0, &array
);
1144 if(SUCCEEDED(hres
)) {
1146 match_result
.cp
= str
;
1149 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1152 TRACE("got match %d %ld\n", (int)(match_result
.cp
- match_result
.match_len
- str
), match_result
.match_len
);
1153 if(!match_result
.match_len
) {
1154 /* If an empty string is matched, prevent including any match in the result */
1159 if(match_result
.cp
== ptr
) {
1161 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1164 TRACE("retried, got match %d %ld\n", (int)(match_result
.cp
- match_result
.match_len
- str
),
1165 match_result
.match_len
);
1167 if(!match_result
.match_len
&& match_result
.cp
== str
+ length
)
1170 ptr2
= match_result
.cp
- match_result
.match_len
;
1171 }else if(match_str
) {
1172 ptr2
= wcsstr(ptr
, match_str
);
1181 if(!regexp
|| ptr2
> ptr
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1182 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1184 hres
= E_OUTOFMEMORY
;
1188 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(tmp_str
));
1189 jsstr_release(tmp_str
);
1195 ptr
= match_result
.cp
;
1197 ptr
= ptr2
+ match_len
;
1203 if(SUCCEEDED(hres
) && (match_str
|| regexp
) && i
<limit
) {
1204 DWORD len
= (str
+length
) - ptr
;
1206 if(len
|| match_str
|| !length
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1207 tmp_str
= jsstr_alloc_len(ptr
, len
);
1210 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1211 jsstr_release(tmp_str
);
1213 hres
= E_OUTOFMEMORY
;
1219 jsdisp_release(regexp
);
1221 jsstr_release(match_jsstr
);
1222 jsstr_release(jsstr
);
1224 if(SUCCEEDED(hres
) && r
)
1225 *r
= jsval_obj(array
);
1227 jsdisp_release(array
);
1232 static HRESULT
String_strike(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1235 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"STRIKE");
1238 static HRESULT
String_sub(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1241 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SUB");
1244 /* ECMA-262 3rd Edition 15.5.4.15 */
1245 static HRESULT
String_substring(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1248 INT start
=0, end
, length
;
1255 hres
= get_string_val(ctx
, vthis
, &str
);
1259 length
= jsstr_length(str
);
1261 hres
= to_integer(ctx
, argv
[0], &d
);
1268 start
= is_int32(d
) ? min(length
, d
) : length
;
1272 hres
= to_integer(ctx
, argv
[1], &d
);
1279 end
= is_int32(d
) ? min(length
, d
) : length
;
1293 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1295 *r
= jsval_string(ret
);
1297 hres
= E_OUTOFMEMORY
;
1303 /* ECMA-262 3rd Edition B.2.3 */
1304 static HRESULT
String_substr(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1307 int start
=0, len
, length
;
1314 hres
= get_string_val(ctx
, vthis
, &str
);
1318 length
= jsstr_length(str
);
1320 hres
= to_integer(ctx
, argv
[0], &d
);
1327 start
= is_int32(d
) ? min(length
, d
) : length
;
1331 hres
= to_integer(ctx
, argv
[1], &d
);
1338 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1347 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1349 *r
= jsval_string(ret
);
1351 hres
= E_OUTOFMEMORY
;
1358 static HRESULT
String_sup(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1361 return do_attributeless_tag_format(ctx
, vthis
, r
, L
"SUP");
1364 static HRESULT
to_upper_case(script_ctx_t
*ctx
, jsval_t vthis
, jsval_t
*r
)
1370 hres
= get_string_val(ctx
, vthis
, &str
);
1375 unsigned len
= jsstr_length(str
);
1379 ret
= jsstr_alloc_buf(len
, &buf
);
1382 return E_OUTOFMEMORY
;
1385 jsstr_flush(str
, buf
);
1386 for (; len
--; buf
++) *buf
= towupper(*buf
);
1388 *r
= jsval_string(ret
);
1394 static HRESULT
to_lower_case(script_ctx_t
*ctx
, jsval_t vthis
, jsval_t
*r
)
1400 hres
= get_string_val(ctx
, vthis
, &str
);
1405 unsigned len
= jsstr_length(str
);
1409 ret
= jsstr_alloc_buf(len
, &buf
);
1412 return E_OUTOFMEMORY
;
1415 jsstr_flush(str
, buf
);
1416 for (; len
--; buf
++) *buf
= towlower(*buf
);
1418 *r
= jsval_string(ret
);
1424 static HRESULT
String_toLowerCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1428 return to_lower_case(ctx
, vthis
, r
);
1431 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1435 return to_upper_case(ctx
, vthis
, r
);
1438 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1442 return to_lower_case(ctx
, vthis
, r
);
1445 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1449 return to_upper_case(ctx
, vthis
, r
);
1452 static HRESULT
String_trim(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
,
1453 jsval_t
*argv
, jsval_t
*r
)
1455 const WCHAR
*str
, *begin
, *end
;
1460 if(is_undefined(vthis
) || is_null(vthis
))
1461 return JS_E_OBJECT_EXPECTED
;
1463 hres
= to_flat_string(ctx
, vthis
, &jsstr
, &str
);
1465 WARN("to_flat_string failed: %08lx\n", hres
);
1468 len
= jsstr_length(jsstr
);
1469 TRACE("%s\n", debugstr_wn(str
, len
));
1471 for(begin
= str
, end
= str
+ len
; begin
< end
&& iswspace(*begin
); begin
++);
1472 while(end
> begin
+ 1 && iswspace(*(end
-1))) end
--;
1477 if(begin
== str
&& end
== str
+ len
)
1478 ret
= jsstr_addref(jsstr
);
1480 ret
= jsstr_alloc_len(begin
, end
- begin
);
1482 *r
= jsval_string(ret
);
1484 hres
= E_OUTOFMEMORY
;
1486 jsstr_release(jsstr
);
1490 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1497 static void String_destructor(jsdisp_t
*dispex
)
1499 StringInstance
*This
= string_from_jsdisp(dispex
);
1501 jsstr_release(This
->str
);
1505 static unsigned String_idx_length(jsdisp_t
*jsdisp
)
1507 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1510 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1511 * Newer jscript.dll versions implement it on string type, not class,
1512 * which is not how it should work according to spec. IE9 implements it
1513 * properly, but it uses its own JavaScript engine inside MSHTML. We
1514 * implement it here, but in the way IE9 and spec work.
1516 return string
->dispex
.ctx
->version
< 2 ? 0 : jsstr_length(string
->str
);
1519 static HRESULT
String_idx_get(jsdisp_t
*jsdisp
, unsigned idx
, jsval_t
*r
)
1521 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1524 ret
= jsstr_substr(string
->str
, idx
, 1);
1526 return E_OUTOFMEMORY
;
1528 TRACE("%p[%u] = %s\n", string
, idx
, debugstr_jsstr(ret
));
1530 *r
= jsval_string(ret
);
1534 static const builtin_prop_t String_props
[] = {
1535 {L
"anchor", String_anchor
, PROPF_METHOD
|1},
1536 {L
"big", String_big
, PROPF_METHOD
},
1537 {L
"blink", String_blink
, PROPF_METHOD
},
1538 {L
"bold", String_bold
, PROPF_METHOD
},
1539 {L
"charAt", String_charAt
, PROPF_METHOD
|1},
1540 {L
"charCodeAt", String_charCodeAt
, PROPF_METHOD
|1},
1541 {L
"concat", String_concat
, PROPF_METHOD
|1},
1542 {L
"fixed", String_fixed
, PROPF_METHOD
},
1543 {L
"fontcolor", String_fontcolor
, PROPF_METHOD
|1},
1544 {L
"fontsize", String_fontsize
, PROPF_METHOD
|1},
1545 {L
"indexOf", String_indexOf
, PROPF_METHOD
|2},
1546 {L
"italics", String_italics
, PROPF_METHOD
},
1547 {L
"lastIndexOf", String_lastIndexOf
, PROPF_METHOD
|2},
1548 {L
"length", NULL
,0, String_get_length
},
1549 {L
"link", String_link
, PROPF_METHOD
|1},
1550 {L
"localeCompare", String_localeCompare
, PROPF_METHOD
|1},
1551 {L
"match", String_match
, PROPF_METHOD
|1},
1552 {L
"replace", String_replace
, PROPF_METHOD
|1},
1553 {L
"search", String_search
, PROPF_METHOD
},
1554 {L
"slice", String_slice
, PROPF_METHOD
},
1555 {L
"small", String_small
, PROPF_METHOD
},
1556 {L
"split", String_split
, PROPF_METHOD
|2},
1557 {L
"strike", String_strike
, PROPF_METHOD
},
1558 {L
"sub", String_sub
, PROPF_METHOD
},
1559 {L
"substr", String_substr
, PROPF_METHOD
|2},
1560 {L
"substring", String_substring
, PROPF_METHOD
|2},
1561 {L
"sup", String_sup
, PROPF_METHOD
},
1562 {L
"toLocaleLowerCase", String_toLocaleLowerCase
, PROPF_METHOD
},
1563 {L
"toLocaleUpperCase", String_toLocaleUpperCase
, PROPF_METHOD
},
1564 {L
"toLowerCase", String_toLowerCase
, PROPF_METHOD
},
1565 {L
"toString", String_toString
, PROPF_METHOD
},
1566 {L
"toUpperCase", String_toUpperCase
, PROPF_METHOD
},
1567 {L
"trim", String_trim
, PROPF_ES5
|PROPF_METHOD
},
1568 {L
"valueOf", String_valueOf
, PROPF_METHOD
}
1571 static const builtin_info_t String_info
= {
1574 ARRAY_SIZE(String_props
),
1580 static const builtin_prop_t StringInst_props
[] = {
1581 {L
"length", NULL
,0, String_get_length
}
1584 static const builtin_info_t StringInst_info
= {
1587 ARRAY_SIZE(StringInst_props
),
1595 /* ECMA-262 3rd Edition 15.5.3.2 */
1596 static HRESULT
StringConstr_fromCharCode(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
,
1597 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1606 ret
= jsstr_alloc_buf(argc
, &ret_str
);
1608 return E_OUTOFMEMORY
;
1610 for(i
=0; i
<argc
; i
++) {
1611 hres
= to_uint32(ctx
, argv
[i
], &code
);
1621 *r
= jsval_string(ret
);
1627 static HRESULT
StringConstr_value(script_ctx_t
*ctx
, jsval_t vthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1630 HRESULT hres
= S_OK
;
1639 hres
= to_string(ctx
, argv
[0], &str
);
1643 str
= jsstr_empty();
1646 if(r
) *r
= jsval_string(str
);
1647 else jsstr_release(str
);
1650 case DISPATCH_CONSTRUCT
: {
1655 hres
= to_string(ctx
, argv
[0], &str
);
1659 str
= jsstr_empty();
1663 hres
= create_string(ctx
, str
, &ret
);
1664 if(SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1671 FIXME("unimplemented flags: %x\n", flags
);
1678 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1680 StringInstance
*string
;
1683 string
= calloc(1, sizeof(StringInstance
));
1685 return E_OUTOFMEMORY
;
1687 if(object_prototype
)
1688 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1690 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1696 string
->str
= jsstr_addref(str
);
1701 static const builtin_prop_t StringConstr_props
[] = {
1702 {L
"fromCharCode", StringConstr_fromCharCode
, PROPF_METHOD
},
1705 static const builtin_info_t StringConstr_info
= {
1708 ARRAY_SIZE(StringConstr_props
),
1714 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1716 StringInstance
*string
;
1719 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1723 hres
= create_builtin_constructor(ctx
, StringConstr_value
, L
"String", &StringConstr_info
,
1724 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1726 jsdisp_release(&string
->dispex
);
1730 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1732 StringInstance
*string
;
1735 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1739 *ret
= &string
->dispex
;