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 hres
= strbuf_append(&ret
, L
"undefined", ARRAY_SIZE(L
"undefined")-1);
898 else if(!match
->match_len
)
903 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
907 jsdisp_release(rep_func
);
909 jsstr_release(rep_jsstr
);
911 jsstr_release(match_jsstr
);
915 if(SUCCEEDED(hres
) && last_match
.cp
&& regexp
) {
916 jsstr_release(ctx
->last_match
);
917 ctx
->last_match
= jsstr_addref(jsstr
);
918 ctx
->last_match_index
= last_match
.cp
-str
-last_match
.match_len
;
919 ctx
->last_match_length
= last_match
.match_len
;
923 jsdisp_release(regexp
);
924 jsstr_release(jsstr
);
926 if(SUCCEEDED(hres
) && r
) {
929 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
931 return E_OUTOFMEMORY
;
933 TRACE("= %s\n", debugstr_jsstr(ret_str
));
934 *r
= jsval_string(ret_str
);
941 static HRESULT
String_search(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
944 jsdisp_t
*regexp
= NULL
;
947 match_state_t match
, *match_ptr
= &match
;
952 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
959 jsstr_release(jsstr
);
963 if(is_object_instance(argv
[0])) {
964 regexp
= iface_to_jsdisp(get_object(argv
[0]));
965 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
966 jsdisp_release(regexp
);
972 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
974 jsstr_release(jsstr
);
980 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
981 jsstr_release(jsstr
);
982 jsdisp_release(regexp
);
987 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
991 /* ECMA-262 3rd Edition 15.5.4.13 */
992 static HRESULT
String_slice(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
995 int start
=0, end
, length
;
1002 hres
= get_string_val(ctx
, jsthis
, &str
);
1006 length
= jsstr_length(str
);
1008 hres
= to_integer(ctx
, argv
[0], &d
);
1017 start
= length
+ start
;
1020 }else if(start
> length
) {
1029 hres
= to_integer(ctx
, argv
[1], &d
);
1041 }else if(end
> length
) {
1045 end
= d
< 0.0 ? 0 : length
;
1055 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1058 return E_OUTOFMEMORY
;
1061 *r
= jsval_string(retstr
);
1068 static HRESULT
String_small(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1071 return do_attributeless_tag_format(ctx
, jsthis
, r
, L
"SMALL");
1074 static HRESULT
String_split(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1077 match_state_t match_result
, *match_ptr
= &match_result
;
1078 size_t length
, i
= 0, match_len
= 0;
1079 const WCHAR
*ptr
, *ptr2
, *str
, *match_str
= NULL
;
1080 unsigned limit
= ~0u;
1081 jsdisp_t
*array
, *regexp
= NULL
;
1082 jsstr_t
*jsstr
, *match_jsstr
, *tmp_str
;
1085 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1088 length
= jsstr_length(jsstr
);
1090 TRACE("%s\n", debugstr_wn(str
, length
));
1092 if(!argc
|| (is_undefined(argv
[0]) && ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
)) {
1096 hres
= create_array(ctx
, 0, &array
);
1100 /* NOTE: according to spec, we should respect limit argument here (if provided).
1101 * We have a test showing that it's broken in native IE. */
1102 hres
= jsdisp_propput_idx(array
, 0, jsval_string(jsstr
));
1104 jsdisp_release(array
);
1108 *r
= jsval_obj(array
);
1112 if(argc
> 1 && !is_undefined(argv
[1])) {
1113 hres
= to_uint32(ctx
, argv
[1], &limit
);
1115 jsstr_release(jsstr
);
1120 if(is_object_instance(argv
[0])) {
1121 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1123 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1124 jsdisp_release(regexp
);
1131 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1133 jsstr_release(jsstr
);
1137 match_len
= jsstr_length(match_jsstr
);
1139 jsstr_release(match_jsstr
);
1144 hres
= create_array(ctx
, 0, &array
);
1146 if(SUCCEEDED(hres
)) {
1148 match_result
.cp
= str
;
1151 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1154 TRACE("got match %d %d\n", (int)(match_result
.cp
- match_result
.match_len
- str
), match_result
.match_len
);
1155 if(!match_result
.match_len
) {
1156 /* If an empty string is matched, prevent including any match in the result */
1161 if(match_result
.cp
== ptr
) {
1163 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1166 TRACE("retried, got match %d %d\n", (int)(match_result
.cp
- match_result
.match_len
- str
),
1167 match_result
.match_len
);
1169 if(!match_result
.match_len
&& match_result
.cp
== str
+ length
)
1172 ptr2
= match_result
.cp
- match_result
.match_len
;
1173 }else if(match_str
) {
1174 ptr2
= wcsstr(ptr
, match_str
);
1183 if(!regexp
|| ptr2
> ptr
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1184 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1186 hres
= E_OUTOFMEMORY
;
1190 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(tmp_str
));
1191 jsstr_release(tmp_str
);
1197 ptr
= match_result
.cp
;
1199 ptr
= ptr2
+ match_len
;
1205 if(SUCCEEDED(hres
) && (match_str
|| regexp
) && i
<limit
) {
1206 DWORD len
= (str
+length
) - ptr
;
1208 if(len
|| match_str
|| !length
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1209 tmp_str
= jsstr_alloc_len(ptr
, len
);
1212 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1213 jsstr_release(tmp_str
);
1215 hres
= E_OUTOFMEMORY
;
1221 jsdisp_release(regexp
);
1223 jsstr_release(match_jsstr
);
1224 jsstr_release(jsstr
);
1226 if(SUCCEEDED(hres
) && r
)
1227 *r
= jsval_obj(array
);
1229 jsdisp_release(array
);
1234 static HRESULT
String_strike(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1237 return do_attributeless_tag_format(ctx
, jsthis
, r
, L
"STRIKE");
1240 static HRESULT
String_sub(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1243 return do_attributeless_tag_format(ctx
, jsthis
, r
, L
"SUB");
1246 /* ECMA-262 3rd Edition 15.5.4.15 */
1247 static HRESULT
String_substring(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1250 INT start
=0, end
, length
;
1257 hres
= get_string_val(ctx
, jsthis
, &str
);
1261 length
= jsstr_length(str
);
1263 hres
= to_integer(ctx
, argv
[0], &d
);
1270 start
= is_int32(d
) ? min(length
, d
) : length
;
1274 hres
= to_integer(ctx
, argv
[1], &d
);
1281 end
= is_int32(d
) ? min(length
, d
) : length
;
1295 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1297 *r
= jsval_string(ret
);
1299 hres
= E_OUTOFMEMORY
;
1305 /* ECMA-262 3rd Edition B.2.3 */
1306 static HRESULT
String_substr(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1309 int start
=0, len
, length
;
1316 hres
= get_string_val(ctx
, jsthis
, &str
);
1320 length
= jsstr_length(str
);
1322 hres
= to_integer(ctx
, argv
[0], &d
);
1329 start
= is_int32(d
) ? min(length
, d
) : length
;
1333 hres
= to_integer(ctx
, argv
[1], &d
);
1340 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1349 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1351 *r
= jsval_string(ret
);
1353 hres
= E_OUTOFMEMORY
;
1360 static HRESULT
String_sup(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1363 return do_attributeless_tag_format(ctx
, jsthis
, r
, L
"SUP");
1366 static HRESULT
String_toLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1374 hres
= get_string_val(ctx
, jsthis
, &str
);
1379 unsigned len
= jsstr_length(str
);
1383 ret
= jsstr_alloc_buf(len
, &buf
);
1386 return E_OUTOFMEMORY
;
1389 jsstr_flush(str
, buf
);
1390 for (; len
--; buf
++) *buf
= towlower(*buf
);
1392 *r
= jsval_string(ret
);
1398 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1406 hres
= get_string_val(ctx
, jsthis
, &str
);
1411 unsigned len
= jsstr_length(str
);
1415 ret
= jsstr_alloc_buf(len
, &buf
);
1418 return E_OUTOFMEMORY
;
1421 jsstr_flush(str
, buf
);
1422 for (; len
--; buf
++) *buf
= towupper(*buf
);
1424 *r
= jsval_string(ret
);
1430 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1437 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1444 static HRESULT
String_trim(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
,
1445 jsval_t
*argv
, jsval_t
*r
)
1447 const WCHAR
*str
, *begin
, *end
;
1452 hres
= to_flat_string(ctx
, jsval_disp(jsthis
->u
.disp
), &jsstr
, &str
);
1454 WARN("to_flat_string failed: %08x\n", hres
);
1457 len
= jsstr_length(jsstr
);
1458 TRACE("%s\n", debugstr_wn(str
, len
));
1460 for(begin
= str
, end
= str
+ len
; begin
< end
&& iswspace(*begin
); begin
++);
1461 while(end
> begin
+ 1 && iswspace(*(end
-1))) end
--;
1466 if(begin
== str
&& end
== str
+ len
)
1467 ret
= jsstr_addref(jsstr
);
1469 ret
= jsstr_alloc_len(begin
, end
- begin
);
1471 *r
= jsval_string(ret
);
1473 hres
= E_OUTOFMEMORY
;
1475 jsstr_release(jsstr
);
1479 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1486 static HRESULT
String_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
1488 StringInstance
*This
= string_from_jsdisp(jsthis
);
1492 *r
= jsval_string(jsstr_addref(This
->str
));
1496 static void String_destructor(jsdisp_t
*dispex
)
1498 StringInstance
*This
= string_from_jsdisp(dispex
);
1500 jsstr_release(This
->str
);
1504 static unsigned String_idx_length(jsdisp_t
*jsdisp
)
1506 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1509 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1510 * Newer jscript.dll versions implement it on string type, not class,
1511 * which is not how it should work according to spec. IE9 implements it
1512 * properly, but it uses its own JavaScript engine inside MSHTML. We
1513 * implement it here, but in the way IE9 and spec work.
1515 return string
->dispex
.ctx
->version
< 2 ? 0 : jsstr_length(string
->str
);
1518 static HRESULT
String_idx_get(jsdisp_t
*jsdisp
, unsigned idx
, jsval_t
*r
)
1520 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1523 ret
= jsstr_substr(string
->str
, idx
, 1);
1525 return E_OUTOFMEMORY
;
1527 TRACE("%p[%u] = %s\n", string
, idx
, debugstr_jsstr(ret
));
1529 *r
= jsval_string(ret
);
1533 static const builtin_prop_t String_props
[] = {
1534 {L
"anchor", String_anchor
, PROPF_METHOD
|1},
1535 {L
"big", String_big
, PROPF_METHOD
},
1536 {L
"blink", String_blink
, PROPF_METHOD
},
1537 {L
"bold", String_bold
, PROPF_METHOD
},
1538 {L
"charAt", String_charAt
, PROPF_METHOD
|1},
1539 {L
"charCodeAt", String_charCodeAt
, PROPF_METHOD
|1},
1540 {L
"concat", String_concat
, PROPF_METHOD
|1},
1541 {L
"fixed", String_fixed
, PROPF_METHOD
},
1542 {L
"fontcolor", String_fontcolor
, PROPF_METHOD
|1},
1543 {L
"fontsize", String_fontsize
, PROPF_METHOD
|1},
1544 {L
"indexOf", String_indexOf
, PROPF_METHOD
|2},
1545 {L
"italics", String_italics
, PROPF_METHOD
},
1546 {L
"lastIndexOf", String_lastIndexOf
, PROPF_METHOD
|2},
1547 {L
"length", NULL
,0, String_get_length
},
1548 {L
"link", String_link
, PROPF_METHOD
|1},
1549 {L
"localeCompare", String_localeCompare
, PROPF_METHOD
|1},
1550 {L
"match", String_match
, PROPF_METHOD
|1},
1551 {L
"replace", String_replace
, PROPF_METHOD
|1},
1552 {L
"search", String_search
, PROPF_METHOD
},
1553 {L
"slice", String_slice
, PROPF_METHOD
},
1554 {L
"small", String_small
, PROPF_METHOD
},
1555 {L
"split", String_split
, PROPF_METHOD
|2},
1556 {L
"strike", String_strike
, PROPF_METHOD
},
1557 {L
"sub", String_sub
, PROPF_METHOD
},
1558 {L
"substr", String_substr
, PROPF_METHOD
|2},
1559 {L
"substring", String_substring
, PROPF_METHOD
|2},
1560 {L
"sup", String_sup
, PROPF_METHOD
},
1561 {L
"toLocaleLowerCase", String_toLocaleLowerCase
, PROPF_METHOD
},
1562 {L
"toLocaleUpperCase", String_toLocaleUpperCase
, PROPF_METHOD
},
1563 {L
"toLowerCase", String_toLowerCase
, PROPF_METHOD
},
1564 {L
"toString", String_toString
, PROPF_METHOD
},
1565 {L
"toUpperCase", String_toUpperCase
, PROPF_METHOD
},
1566 {L
"trim", String_trim
, PROPF_ES5
|PROPF_METHOD
},
1567 {L
"valueOf", String_valueOf
, PROPF_METHOD
}
1570 static const builtin_info_t String_info
= {
1572 {NULL
, NULL
,0, String_get_value
},
1573 ARRAY_SIZE(String_props
),
1579 static const builtin_prop_t StringInst_props
[] = {
1580 {L
"length", NULL
,0, String_get_length
}
1583 static const builtin_info_t StringInst_info
= {
1585 {NULL
, NULL
,0, String_get_value
},
1586 ARRAY_SIZE(StringInst_props
),
1594 /* ECMA-262 3rd Edition 15.5.3.2 */
1595 static HRESULT
StringConstr_fromCharCode(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
1596 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1605 ret
= jsstr_alloc_buf(argc
, &ret_str
);
1607 return E_OUTOFMEMORY
;
1609 for(i
=0; i
<argc
; i
++) {
1610 hres
= to_uint32(ctx
, argv
[i
], &code
);
1620 *r
= jsval_string(ret
);
1626 static HRESULT
StringConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1638 hres
= to_string(ctx
, argv
[0], &str
);
1642 str
= jsstr_empty();
1645 *r
= jsval_string(str
);
1648 case DISPATCH_CONSTRUCT
: {
1653 hres
= to_string(ctx
, argv
[0], &str
);
1657 str
= jsstr_empty();
1660 hres
= create_string(ctx
, str
, &ret
);
1661 if (SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1667 FIXME("unimplemented flags: %x\n", flags
);
1674 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1676 StringInstance
*string
;
1679 string
= heap_alloc_zero(sizeof(StringInstance
));
1681 return E_OUTOFMEMORY
;
1683 if(object_prototype
)
1684 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1686 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1692 string
->str
= jsstr_addref(str
);
1697 static const builtin_prop_t StringConstr_props
[] = {
1698 {L
"fromCharCode", StringConstr_fromCharCode
, PROPF_METHOD
},
1701 static const builtin_info_t StringConstr_info
= {
1703 DEFAULT_FUNCTION_VALUE
,
1704 ARRAY_SIZE(StringConstr_props
),
1710 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1712 StringInstance
*string
;
1715 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1719 hres
= create_builtin_constructor(ctx
, StringConstr_value
, L
"String", &StringConstr_info
,
1720 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1722 jsdisp_release(&string
->dispex
);
1726 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1728 StringInstance
*string
;
1731 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1735 *ret
= &string
->dispex
;