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_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
);
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
)
65 hres
= get_string_val(ctx
, jsthis
, jsval
);
69 *val
= jsstr_flatten(*jsval
);
73 jsstr_release(*jsval
);
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
));
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");
97 *r
= jsval_string(jsstr_addref(string
->str
));
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
,
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
,
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
;
126 hres
= get_string_val(ctx
, jsthis
, &str
);
135 tagname_len
= lstrlenW(tagname
);
137 ret
= jsstr_alloc_buf(jsstr_length(str
) + 2*tagname_len
+ 5, &ptr
);
140 return E_OUTOFMEMORY
;
144 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
148 ptr
+= jsstr_flush(str
, ptr
);
153 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
157 *r
= jsval_string(ret
);
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
;
167 hres
= get_string_val(ctx
, jsthis
, &str
);
172 hres
= to_string(ctx
, argv
[0], &attr_value
);
178 attr_value
= jsstr_undefined();
182 unsigned attrname_len
= lstrlenW(attrname
);
183 unsigned tagname_len
= lstrlenW(tagname
);
187 ret
= jsstr_alloc_buf(2*tagname_len
+ attrname_len
+ jsstr_length(attr_value
) + jsstr_length(str
) + 9, &ptr
);
190 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
193 memcpy(ptr
, attrname
, attrname_len
*sizeof(WCHAR
));
197 ptr
+= jsstr_flush(attr_value
, ptr
);
200 ptr
+= jsstr_flush(str
, ptr
);
204 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
208 *r
= jsval_string(ret
);
210 hres
= E_OUTOFMEMORY
;
214 jsstr_release(attr_value
);
219 static HRESULT
String_anchor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
,
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
,
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
,
253 hres
= get_string_val(ctx
, jsthis
, &str
);
260 hres
= to_integer(ctx
, argv
[0], &d
);
265 pos
= is_int32(d
) ? d
: -1;
273 if(0 <= pos
&& pos
< jsstr_length(str
)) {
274 ret
= jsstr_substr(str
, pos
, 1);
276 return E_OUTOFMEMORY
;
281 *r
= jsval_string(ret
);
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
,
295 hres
= get_string_val(ctx
, jsthis
, &str
);
302 hres
= to_integer(ctx
, argv
[0], &d
);
308 if(!is_int32(d
) || d
< 0 || d
>= jsstr_length(str
)) {
311 *r
= jsval_number(NAN
);
320 jsstr_extract(str
, idx
, 1, &c
);
321 *r
= jsval_number(c
);
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
,
332 jsstr_t
*ret
= NULL
, *str
;
337 hres
= get_string_val(ctx
, jsthis
, &str
);
348 hres
= to_string(ctx
, argv
[0], &arg_str
);
354 ret
= jsstr_concat(str
, arg_str
);
357 return E_OUTOFMEMORY
;
361 const unsigned str_cnt
= argc
+1;
366 strs
= heap_alloc_zero(str_cnt
* sizeof(*strs
));
369 return E_OUTOFMEMORY
;
373 for(i
=0; i
< argc
; i
++) {
374 hres
= to_string(ctx
, argv
[i
], strs
+i
+1);
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
;
388 if(SUCCEEDED(hres
)) {
389 ret
= jsstr_alloc_buf(len
, &ptr
);
391 for(i
=0; i
< str_cnt
; i
++)
392 ptr
+= jsstr_flush(strs
[i
], ptr
);
394 hres
= E_OUTOFMEMORY
;
400 jsstr_release(strs
[i
]);
408 *r
= jsval_string(ret
);
414 static HRESULT
String_fixed(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
,
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
,
435 unsigned pos
= 0, search_len
, length
;
436 jsstr_t
*search_jsstr
, *jsstr
;
437 const WCHAR
*search_str
, *str
;
443 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
449 *r
= jsval_number(-1);
450 jsstr_release(jsstr
);
454 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
456 jsstr_release(jsstr
);
460 search_len
= jsstr_length(search_jsstr
);
461 length
= jsstr_length(jsstr
);
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
;
475 for(ptr
= str
+pos
; ptr
<= end
; ptr
++) {
476 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
483 jsstr_release(search_jsstr
);
484 jsstr_release(jsstr
);
489 *r
= jsval_number(ret
);
493 static HRESULT
String_italics(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
503 unsigned pos
= 0, search_len
, length
;
504 jsstr_t
*search_jsstr
, *jsstr
;
505 const WCHAR
*search_str
, *str
;
511 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
517 *r
= jsval_number(-1);
518 jsstr_release(jsstr
);
522 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
524 jsstr_release(jsstr
);
528 search_len
= jsstr_length(search_jsstr
);
529 length
= jsstr_length(jsstr
);
534 hres
= to_integer(ctx
, argv
[1], &d
);
535 if(SUCCEEDED(hres
) && d
> 0)
536 pos
= is_int32(d
) ? min(length
, d
) : length
;
541 if(SUCCEEDED(hres
) && length
>= search_len
) {
544 for(ptr
= str
+min(pos
, length
-search_len
); ptr
>= str
; ptr
--) {
545 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
552 jsstr_release(search_jsstr
);
553 jsstr_release(jsstr
);
558 *r
= jsval_number(ret
);
562 static HRESULT
String_link(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
572 jsdisp_t
*regexp
= NULL
;
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
);
595 hres
= to_string(ctx
, argv
[0], &match_str
);
599 hres
= create_regexp(ctx
, match_str
, 0, ®exp
);
600 jsstr_release(match_str
);
605 hres
= get_string_val(ctx
, jsthis
, &str
);
607 hres
= regexp_string_match(ctx
, regexp
, str
, r
);
609 jsdisp_release(regexp
);
620 static BOOL
strbuf_ensure_size(strbuf_t
*buf
, unsigned len
)
628 new_size
= buf
->size
? buf
->size
<<1 : 16;
632 new_buf
= heap_realloc(buf
->buf
, new_size
*sizeof(WCHAR
));
634 new_buf
= heap_alloc(new_size
*sizeof(WCHAR
));
639 buf
->size
= new_size
;
643 static HRESULT
strbuf_append(strbuf_t
*buf
, const WCHAR
*str
, DWORD len
)
648 if(!strbuf_ensure_size(buf
, buf
->len
+len
))
649 return E_OUTOFMEMORY
;
651 memcpy(buf
->buf
+buf
->len
, str
, len
*sizeof(WCHAR
));
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
);
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
)
676 argc
= match
->paren_count
+3;
677 argv
= heap_alloc_zero(sizeof(*argv
)*argc
);
679 return E_OUTOFMEMORY
;
681 tmp_str
= jsstr_alloc_len(match
->cp
-match
->match_len
, match
->match_len
);
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
);
691 tmp_str
= jsstr_empty();
693 hres
= E_OUTOFMEMORY
;
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
);
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
]));
715 hres
= to_string(ctx
, val
, ret
);
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
,
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
;
735 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
741 *r
= jsval_string(jsstr
);
743 jsstr_release(jsstr
);
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
);
756 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
758 jsstr_release(jsstr
);
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
);
773 hres
= to_flat_string(ctx
, argv
[1], &rep_jsstr
, &rep_str
);
775 rep_len
= jsstr_length(rep_jsstr
);
779 if(SUCCEEDED(hres
)) {
780 const WCHAR
*ecp
= str
;
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
) {
794 last_match
.cp
= match
->cp
;
795 last_match
.match_len
= match
->match_len
;
797 if(re_flags
& REM_ALLOC_RESULT
) {
798 re_flags
&= ~REM_ALLOC_RESULT
;
803 match
->cp
= wcsstr(match
->cp
, match_str
);
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
);
818 hres
= rep_call(ctx
, rep_func
, jsstr
, str
, match
, &cstr
);
822 hres
= strbuf_append_jsstr(&ret
, cstr
);
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
);
836 hres
= strbuf_append(&ret
, ptr2
, 1);
840 hres
= strbuf_append(&ret
, match
->cp
-match
->match_len
, match
->match_len
);
844 hres
= strbuf_append(&ret
, str
, match
->cp
-str
-match
->match_len
);
848 hres
= strbuf_append(&ret
, ecp
, (str
+jsstr_length(jsstr
))-ecp
);
854 if(!is_digit(ptr2
[1])) {
855 hres
= strbuf_append(&ret
, ptr2
, 1);
861 if(is_digit(ptr2
[2]) && idx
*10 + (ptr2
[2]-'0') <= match
->paren_count
) {
862 idx
= idx
*10 + (ptr
[2]-'0');
864 }else if(idx
&& idx
<= match
->paren_count
) {
867 hres
= strbuf_append(&ret
, ptr2
, 1);
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
);
883 hres
= strbuf_append(&ret
, ptr
, (rep_str
+rep_len
)-ptr
);
887 hres
= strbuf_append(&ret
, rep_str
, rep_len
);
891 static const WCHAR undefinedW
[] = {'u','n','d','e','f','i','n','e','d'};
893 hres
= strbuf_append(&ret
, undefinedW
, ARRAY_SIZE(undefinedW
));
900 else if(!match
->match_len
)
905 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
909 jsdisp_release(rep_func
);
911 jsstr_release(rep_jsstr
);
913 jsstr_release(match_jsstr
);
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
;
925 jsdisp_release(regexp
);
926 jsstr_release(jsstr
);
928 if(SUCCEEDED(hres
) && r
) {
931 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
933 return E_OUTOFMEMORY
;
935 TRACE("= %s\n", debugstr_jsstr(ret_str
));
936 *r
= jsval_string(ret_str
);
943 static HRESULT
String_search(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
946 jsdisp_t
*regexp
= NULL
;
949 match_state_t match
, *match_ptr
= &match
;
954 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
961 jsstr_release(jsstr
);
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
);
974 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
976 jsstr_release(jsstr
);
982 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
983 jsstr_release(jsstr
);
984 jsdisp_release(regexp
);
989 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
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
,
997 int start
=0, end
, length
;
1004 hres
= get_string_val(ctx
, jsthis
, &str
);
1008 length
= jsstr_length(str
);
1010 hres
= to_integer(ctx
, argv
[0], &d
);
1019 start
= length
+ start
;
1022 }else if(start
> length
) {
1031 hres
= to_integer(ctx
, argv
[1], &d
);
1043 }else if(end
> length
) {
1047 end
= d
< 0.0 ? 0 : length
;
1057 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1060 return E_OUTOFMEMORY
;
1063 *r
= jsval_string(retstr
);
1070 static HRESULT
String_small(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
;
1087 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1090 length
= jsstr_length(jsstr
);
1092 TRACE("%s\n", debugstr_wn(str
, length
));
1094 if(!argc
|| (is_undefined(argv
[0]) && ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
)) {
1098 hres
= create_array(ctx
, 0, &array
);
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
));
1106 jsdisp_release(array
);
1110 *r
= jsval_obj(array
);
1114 if(argc
> 1 && !is_undefined(argv
[1])) {
1115 hres
= to_uint32(ctx
, argv
[1], &limit
);
1117 jsstr_release(jsstr
);
1122 if(is_object_instance(argv
[0])) {
1123 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1125 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1126 jsdisp_release(regexp
);
1133 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1135 jsstr_release(jsstr
);
1139 match_len
= jsstr_length(match_jsstr
);
1141 jsstr_release(match_jsstr
);
1146 hres
= create_array(ctx
, 0, &array
);
1148 if(SUCCEEDED(hres
)) {
1150 match_result
.cp
= str
;
1153 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
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 */
1163 if(match_result
.cp
== ptr
) {
1165 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
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
)
1174 ptr2
= match_result
.cp
- match_result
.match_len
;
1175 }else if(match_str
) {
1176 ptr2
= wcsstr(ptr
, match_str
);
1185 if(!regexp
|| ptr2
> ptr
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1186 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1188 hres
= E_OUTOFMEMORY
;
1192 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(tmp_str
));
1193 jsstr_release(tmp_str
);
1199 ptr
= match_result
.cp
;
1201 ptr
= ptr2
+ match_len
;
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
);
1214 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1215 jsstr_release(tmp_str
);
1217 hres
= E_OUTOFMEMORY
;
1223 jsdisp_release(regexp
);
1225 jsstr_release(match_jsstr
);
1226 jsstr_release(jsstr
);
1228 if(SUCCEEDED(hres
) && r
)
1229 *r
= jsval_obj(array
);
1231 jsdisp_release(array
);
1236 static HRESULT
String_strike(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
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
,
1252 INT start
=0, end
, length
;
1259 hres
= get_string_val(ctx
, jsthis
, &str
);
1263 length
= jsstr_length(str
);
1265 hres
= to_integer(ctx
, argv
[0], &d
);
1272 start
= is_int32(d
) ? min(length
, d
) : length
;
1276 hres
= to_integer(ctx
, argv
[1], &d
);
1283 end
= is_int32(d
) ? min(length
, d
) : length
;
1297 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1299 *r
= jsval_string(ret
);
1301 hres
= E_OUTOFMEMORY
;
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
,
1311 int start
=0, len
, length
;
1318 hres
= get_string_val(ctx
, jsthis
, &str
);
1322 length
= jsstr_length(str
);
1324 hres
= to_integer(ctx
, argv
[0], &d
);
1331 start
= is_int32(d
) ? min(length
, d
) : length
;
1335 hres
= to_integer(ctx
, argv
[1], &d
);
1342 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1351 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1353 *r
= jsval_string(ret
);
1355 hres
= E_OUTOFMEMORY
;
1362 static HRESULT
String_sup(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
,
1376 hres
= get_string_val(ctx
, jsthis
, &str
);
1381 unsigned len
= jsstr_length(str
);
1385 ret
= jsstr_alloc_buf(len
, &buf
);
1388 return E_OUTOFMEMORY
;
1391 jsstr_flush(str
, buf
);
1392 for (; len
--; buf
++) *buf
= towlower(*buf
);
1394 *r
= jsval_string(ret
);
1400 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1408 hres
= get_string_val(ctx
, jsthis
, &str
);
1413 unsigned len
= jsstr_length(str
);
1417 ret
= jsstr_alloc_buf(len
, &buf
);
1420 return E_OUTOFMEMORY
;
1423 jsstr_flush(str
, buf
);
1424 for (; len
--; buf
++) *buf
= towupper(*buf
);
1426 *r
= jsval_string(ret
);
1432 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1439 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
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
;
1454 hres
= to_flat_string(ctx
, jsval_disp(jsthis
->u
.disp
), &jsstr
, &str
);
1456 WARN("to_flat_string failed: %08x\n", 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
--;
1468 if(begin
== str
&& end
== str
+ len
)
1469 ret
= jsstr_addref(jsstr
);
1471 ret
= jsstr_alloc_len(begin
, end
- begin
);
1473 *r
= jsval_string(ret
);
1475 hres
= E_OUTOFMEMORY
;
1477 jsstr_release(jsstr
);
1481 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1488 static HRESULT
String_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
1490 StringInstance
*This
= string_from_jsdisp(jsthis
);
1494 *r
= jsval_string(jsstr_addref(This
->str
));
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
= {
1574 {NULL
, NULL
,0, String_get_value
},
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
= {
1587 {NULL
, NULL
,0, String_get_value
},
1588 ARRAY_SIZE(StringInst_props
),
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
)
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
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1640 hres
= to_string(ctx
, argv
[0], &str
);
1644 str
= jsstr_empty();
1647 *r
= jsval_string(str
);
1650 case DISPATCH_CONSTRUCT
: {
1655 hres
= to_string(ctx
, argv
[0], &str
);
1659 str
= jsstr_empty();
1662 hres
= create_string(ctx
, str
, &ret
);
1663 if (SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1669 FIXME("unimplemented flags: %x\n", flags
);
1676 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1678 StringInstance
*string
;
1681 string
= heap_alloc_zero(sizeof(StringInstance
));
1683 return E_OUTOFMEMORY
;
1685 if(object_prototype
)
1686 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1688 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1694 string
->str
= jsstr_addref(str
);
1699 static const builtin_prop_t StringConstr_props
[] = {
1700 {L
"fromCharCode", StringConstr_fromCharCode
, PROPF_METHOD
},
1703 static const builtin_info_t StringConstr_info
= {
1705 DEFAULT_FUNCTION_VALUE
,
1706 ARRAY_SIZE(StringConstr_props
),
1712 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1714 StringInstance
*string
;
1717 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1721 hres
= create_builtin_constructor(ctx
, StringConstr_value
, L
"String", &StringConstr_info
,
1722 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1724 jsdisp_release(&string
->dispex
);
1728 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1730 StringInstance
*string
;
1733 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1737 *ret
= &string
->dispex
;