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 const WCHAR lengthW
[] = {'l','e','n','g','t','h',0};
35 static const WCHAR toStringW
[] = {'t','o','S','t','r','i','n','g',0};
36 static const WCHAR valueOfW
[] = {'v','a','l','u','e','O','f',0};
37 static const WCHAR anchorW
[] = {'a','n','c','h','o','r',0};
38 static const WCHAR bigW
[] = {'b','i','g',0};
39 static const WCHAR blinkW
[] = {'b','l','i','n','k',0};
40 static const WCHAR boldW
[] = {'b','o','l','d',0};
41 static const WCHAR charAtW
[] = {'c','h','a','r','A','t',0};
42 static const WCHAR charCodeAtW
[] = {'c','h','a','r','C','o','d','e','A','t',0};
43 static const WCHAR concatW
[] = {'c','o','n','c','a','t',0};
44 static const WCHAR fixedW
[] = {'f','i','x','e','d',0};
45 static const WCHAR fontcolorW
[] = {'f','o','n','t','c','o','l','o','r',0};
46 static const WCHAR fontsizeW
[] = {'f','o','n','t','s','i','z','e',0};
47 static const WCHAR indexOfW
[] = {'i','n','d','e','x','O','f',0};
48 static const WCHAR italicsW
[] = {'i','t','a','l','i','c','s',0};
49 static const WCHAR lastIndexOfW
[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
50 static const WCHAR linkW
[] = {'l','i','n','k',0};
51 static const WCHAR matchW
[] = {'m','a','t','c','h',0};
52 static const WCHAR replaceW
[] = {'r','e','p','l','a','c','e',0};
53 static const WCHAR searchW
[] = {'s','e','a','r','c','h',0};
54 static const WCHAR sliceW
[] = {'s','l','i','c','e',0};
55 static const WCHAR smallW
[] = {'s','m','a','l','l',0};
56 static const WCHAR splitW
[] = {'s','p','l','i','t',0};
57 static const WCHAR strikeW
[] = {'s','t','r','i','k','e',0};
58 static const WCHAR subW
[] = {'s','u','b',0};
59 static const WCHAR substringW
[] = {'s','u','b','s','t','r','i','n','g',0};
60 static const WCHAR substrW
[] = {'s','u','b','s','t','r',0};
61 static const WCHAR supW
[] = {'s','u','p',0};
62 static const WCHAR toLowerCaseW
[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
63 static const WCHAR toUpperCaseW
[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
64 static const WCHAR toLocaleLowerCaseW
[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
65 static const WCHAR toLocaleUpperCaseW
[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
66 static const WCHAR trimW
[] = {'t','r','i','m',0};
67 static const WCHAR localeCompareW
[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
68 static const WCHAR fromCharCodeW
[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
70 static inline StringInstance
*string_from_jsdisp(jsdisp_t
*jsdisp
)
72 return CONTAINING_RECORD(jsdisp
, StringInstance
, dispex
);
75 static inline StringInstance
*string_from_vdisp(vdisp_t
*vdisp
)
77 return string_from_jsdisp(vdisp
->u
.jsdisp
);
80 static inline StringInstance
*string_this(vdisp_t
*jsthis
)
82 return is_vclass(jsthis
, JSCLASS_STRING
) ? string_from_vdisp(jsthis
) : NULL
;
85 static HRESULT
get_string_val(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsstr_t
**val
)
87 StringInstance
*string
;
89 if((string
= string_this(jsthis
))) {
90 *val
= jsstr_addref(string
->str
);
94 return to_string(ctx
, jsval_disp(jsthis
->u
.disp
), val
);
97 static HRESULT
get_string_flat_val(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsstr_t
**jsval
, const WCHAR
**val
)
101 hres
= get_string_val(ctx
, jsthis
, jsval
);
105 *val
= jsstr_flatten(*jsval
);
109 jsstr_release(*jsval
);
110 return E_OUTOFMEMORY
;
113 static HRESULT
String_get_length(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
115 StringInstance
*string
= string_from_jsdisp(jsthis
);
117 TRACE("%p\n", jsthis
);
119 *r
= jsval_number(jsstr_length(string
->str
));
123 static HRESULT
stringobj_to_string(vdisp_t
*jsthis
, jsval_t
*r
)
125 StringInstance
*string
;
127 if(!(string
= string_this(jsthis
))) {
128 WARN("this is not a string object\n");
133 *r
= jsval_string(jsstr_addref(string
->str
));
137 /* ECMA-262 3rd Edition 15.5.4.2 */
138 static HRESULT
String_toString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
143 return stringobj_to_string(jsthis
, r
);
146 /* ECMA-262 3rd Edition 15.5.4.2 */
147 static HRESULT
String_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
152 return stringobj_to_string(jsthis
, r
);
155 static HRESULT
do_attributeless_tag_format(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsval_t
*r
, const WCHAR
*tagname
)
157 unsigned tagname_len
;
162 hres
= get_string_val(ctx
, jsthis
, &str
);
171 tagname_len
= lstrlenW(tagname
);
173 ret
= jsstr_alloc_buf(jsstr_length(str
) + 2*tagname_len
+ 5, &ptr
);
176 return E_OUTOFMEMORY
;
180 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
184 ptr
+= jsstr_flush(str
, ptr
);
189 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
193 *r
= jsval_string(ret
);
197 static HRESULT
do_attribute_tag_format(script_ctx_t
*ctx
, vdisp_t
*jsthis
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
,
198 const WCHAR
*tagname
, const WCHAR
*attrname
)
200 jsstr_t
*str
, *attr_value
= NULL
;
203 hres
= get_string_val(ctx
, jsthis
, &str
);
208 hres
= to_string(ctx
, argv
[0], &attr_value
);
214 attr_value
= jsstr_undefined();
218 unsigned attrname_len
= lstrlenW(attrname
);
219 unsigned tagname_len
= lstrlenW(tagname
);
223 ret
= jsstr_alloc_buf(2*tagname_len
+ attrname_len
+ jsstr_length(attr_value
) + jsstr_length(str
) + 9, &ptr
);
226 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
229 memcpy(ptr
, attrname
, attrname_len
*sizeof(WCHAR
));
233 ptr
+= jsstr_flush(attr_value
, ptr
);
236 ptr
+= jsstr_flush(str
, ptr
);
240 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
244 *r
= jsval_string(ret
);
246 hres
= E_OUTOFMEMORY
;
250 jsstr_release(attr_value
);
255 static HRESULT
String_anchor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
258 static const WCHAR fontW
[] = {'A',0};
259 static const WCHAR colorW
[] = {'N','A','M','E',0};
261 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
264 static HRESULT
String_big(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
267 static const WCHAR bigtagW
[] = {'B','I','G',0};
268 return do_attributeless_tag_format(ctx
, jsthis
, r
, bigtagW
);
271 static HRESULT
String_blink(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
274 static const WCHAR blinktagW
[] = {'B','L','I','N','K',0};
275 return do_attributeless_tag_format(ctx
, jsthis
, r
, blinktagW
);
278 static HRESULT
String_bold(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
281 static const WCHAR boldtagW
[] = {'B',0};
282 return do_attributeless_tag_format(ctx
, jsthis
, r
, boldtagW
);
285 /* ECMA-262 3rd Edition 15.5.4.5 */
286 static HRESULT
String_charAt(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
);
307 pos
= is_int32(d
) ? d
: -1;
315 if(0 <= pos
&& pos
< jsstr_length(str
)) {
316 ret
= jsstr_substr(str
, pos
, 1);
318 return E_OUTOFMEMORY
;
323 *r
= jsval_string(ret
);
327 /* ECMA-262 3rd Edition 15.5.4.5 */
328 static HRESULT
String_charCodeAt(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
337 hres
= get_string_val(ctx
, jsthis
, &str
);
344 hres
= to_integer(ctx
, argv
[0], &d
);
350 if(!is_int32(d
) || d
< 0 || d
>= jsstr_length(str
)) {
353 *r
= jsval_number(NAN
);
362 jsstr_extract(str
, idx
, 1, &c
);
363 *r
= jsval_number(c
);
370 /* ECMA-262 3rd Edition 15.5.4.6 */
371 static HRESULT
String_concat(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
374 jsstr_t
*ret
= NULL
, *str
;
379 hres
= get_string_val(ctx
, jsthis
, &str
);
390 hres
= to_string(ctx
, argv
[0], &arg_str
);
396 ret
= jsstr_concat(str
, arg_str
);
399 return E_OUTOFMEMORY
;
403 const unsigned str_cnt
= argc
+1;
408 strs
= heap_alloc_zero(str_cnt
* sizeof(*strs
));
411 return E_OUTOFMEMORY
;
415 for(i
=0; i
< argc
; i
++) {
416 hres
= to_string(ctx
, argv
[i
], strs
+i
+1);
421 if(SUCCEEDED(hres
)) {
422 for(i
=0; i
< str_cnt
; i
++) {
423 len
+= jsstr_length(strs
[i
]);
424 if(len
> JSSTR_MAX_LENGTH
) {
425 hres
= E_OUTOFMEMORY
;
430 if(SUCCEEDED(hres
)) {
431 ret
= jsstr_alloc_buf(len
, &ptr
);
433 for(i
=0; i
< str_cnt
; i
++)
434 ptr
+= jsstr_flush(strs
[i
], ptr
);
436 hres
= E_OUTOFMEMORY
;
442 jsstr_release(strs
[i
]);
450 *r
= jsval_string(ret
);
456 static HRESULT
String_fixed(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
459 static const WCHAR fixedtagW
[] = {'T','T',0};
460 return do_attributeless_tag_format(ctx
, jsthis
, r
, fixedtagW
);
463 static HRESULT
String_fontcolor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
466 static const WCHAR fontW
[] = {'F','O','N','T',0};
467 static const WCHAR colorW
[] = {'C','O','L','O','R',0};
469 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
472 static HRESULT
String_fontsize(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
475 static const WCHAR fontW
[] = {'F','O','N','T',0};
476 static const WCHAR colorW
[] = {'S','I','Z','E',0};
478 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
481 static HRESULT
String_indexOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
484 unsigned pos
= 0, search_len
, length
;
485 jsstr_t
*search_jsstr
, *jsstr
;
486 const WCHAR
*search_str
, *str
;
492 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
498 *r
= jsval_number(-1);
499 jsstr_release(jsstr
);
503 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
505 jsstr_release(jsstr
);
509 search_len
= jsstr_length(search_jsstr
);
510 length
= jsstr_length(jsstr
);
515 hres
= to_integer(ctx
, argv
[1], &d
);
516 if(SUCCEEDED(hres
) && d
> 0.0)
517 pos
= is_int32(d
) ? min(length
, d
) : length
;
520 if(SUCCEEDED(hres
) && length
>= search_len
) {
521 const WCHAR
*end
= str
+length
-search_len
;
524 for(ptr
= str
+pos
; ptr
<= end
; ptr
++) {
525 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
532 jsstr_release(search_jsstr
);
533 jsstr_release(jsstr
);
538 *r
= jsval_number(ret
);
542 static HRESULT
String_italics(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
545 static const WCHAR italicstagW
[] = {'I',0};
546 return do_attributeless_tag_format(ctx
, jsthis
, r
, italicstagW
);
549 /* ECMA-262 3rd Edition 15.5.4.8 */
550 static HRESULT
String_lastIndexOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
553 unsigned pos
= 0, search_len
, length
;
554 jsstr_t
*search_jsstr
, *jsstr
;
555 const WCHAR
*search_str
, *str
;
561 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
567 *r
= jsval_number(-1);
568 jsstr_release(jsstr
);
572 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
574 jsstr_release(jsstr
);
578 search_len
= jsstr_length(search_jsstr
);
579 length
= jsstr_length(jsstr
);
584 hres
= to_integer(ctx
, argv
[1], &d
);
585 if(SUCCEEDED(hres
) && d
> 0)
586 pos
= is_int32(d
) ? min(length
, d
) : length
;
591 if(SUCCEEDED(hres
) && length
>= search_len
) {
594 for(ptr
= str
+min(pos
, length
-search_len
); ptr
>= str
; ptr
--) {
595 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
602 jsstr_release(search_jsstr
);
603 jsstr_release(jsstr
);
608 *r
= jsval_number(ret
);
612 static HRESULT
String_link(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
615 static const WCHAR fontW
[] = {'A',0};
616 static const WCHAR colorW
[] = {'H','R','E','F',0};
618 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
621 /* ECMA-262 3rd Edition 15.5.4.10 */
622 static HRESULT
String_match(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
625 jsdisp_t
*regexp
= NULL
;
637 if(is_object_instance(argv
[0])) {
638 regexp
= iface_to_jsdisp(get_object(argv
[0]));
639 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
640 jsdisp_release(regexp
);
648 hres
= to_string(ctx
, argv
[0], &match_str
);
652 hres
= create_regexp(ctx
, match_str
, 0, ®exp
);
653 jsstr_release(match_str
);
658 hres
= get_string_val(ctx
, jsthis
, &str
);
660 hres
= regexp_string_match(ctx
, regexp
, str
, r
);
662 jsdisp_release(regexp
);
673 static BOOL
strbuf_ensure_size(strbuf_t
*buf
, unsigned len
)
681 new_size
= buf
->size
? buf
->size
<<1 : 16;
685 new_buf
= heap_realloc(buf
->buf
, new_size
*sizeof(WCHAR
));
687 new_buf
= heap_alloc(new_size
*sizeof(WCHAR
));
692 buf
->size
= new_size
;
696 static HRESULT
strbuf_append(strbuf_t
*buf
, const WCHAR
*str
, DWORD len
)
701 if(!strbuf_ensure_size(buf
, buf
->len
+len
))
702 return E_OUTOFMEMORY
;
704 memcpy(buf
->buf
+buf
->len
, str
, len
*sizeof(WCHAR
));
709 static HRESULT
strbuf_append_jsstr(strbuf_t
*buf
, jsstr_t
*str
)
711 if(!strbuf_ensure_size(buf
, buf
->len
+jsstr_length(str
)))
712 return E_OUTOFMEMORY
;
714 jsstr_flush(str
, buf
->buf
+buf
->len
);
715 buf
->len
+= jsstr_length(str
);
719 static HRESULT
rep_call(script_ctx_t
*ctx
, jsdisp_t
*func
,
720 jsstr_t
*jsstr
, const WCHAR
*str
, match_state_t
*match
, jsstr_t
**ret
)
729 argc
= match
->paren_count
+3;
730 argv
= heap_alloc_zero(sizeof(*argv
)*argc
);
732 return E_OUTOFMEMORY
;
734 tmp_str
= jsstr_alloc_len(match
->cp
-match
->match_len
, match
->match_len
);
736 hres
= E_OUTOFMEMORY
;
737 argv
[0] = jsval_string(tmp_str
);
739 if(SUCCEEDED(hres
)) {
740 for(i
=0; i
< match
->paren_count
; i
++) {
741 if(match
->parens
[i
].index
!= -1)
742 tmp_str
= jsstr_substr(jsstr
, match
->parens
[i
].index
, match
->parens
[i
].length
);
744 tmp_str
= jsstr_empty();
746 hres
= E_OUTOFMEMORY
;
749 argv
[i
+1] = jsval_string(tmp_str
);
753 if(SUCCEEDED(hres
)) {
754 argv
[match
->paren_count
+1] = jsval_number(match
->cp
-str
- match
->match_len
);
755 argv
[match
->paren_count
+2] = jsval_string(jsstr
);
759 hres
= jsdisp_call_value(func
, NULL
, DISPATCH_METHOD
, argc
, argv
, &val
);
761 for(i
=0; i
<= match
->paren_count
; i
++)
762 jsstr_release(get_string(argv
[i
]));
768 hres
= to_string(ctx
, val
, ret
);
773 /* ECMA-262 3rd Edition 15.5.4.11 */
774 static HRESULT
String_replace(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
777 const WCHAR
*str
, *match_str
= NULL
, *rep_str
= NULL
;
778 jsstr_t
*rep_jsstr
, *match_jsstr
, *jsstr
;
779 jsdisp_t
*rep_func
= NULL
, *regexp
= NULL
;
780 match_state_t
*match
= NULL
, last_match
= {0};
781 strbuf_t ret
= {NULL
,0,0};
782 DWORD re_flags
= REM_NO_CTX_UPDATE
|REM_ALLOC_RESULT
;
788 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
794 *r
= jsval_string(jsstr
);
796 jsstr_release(jsstr
);
800 if(is_object_instance(argv
[0])) {
801 regexp
= iface_to_jsdisp(get_object(argv
[0]));
802 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
803 jsdisp_release(regexp
);
809 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
811 jsstr_release(jsstr
);
817 if(is_object_instance(argv
[1])) {
818 rep_func
= iface_to_jsdisp(get_object(argv
[1]));
819 if(rep_func
&& !is_class(rep_func
, JSCLASS_FUNCTION
)) {
820 jsdisp_release(rep_func
);
826 hres
= to_flat_string(ctx
, argv
[1], &rep_jsstr
, &rep_str
);
828 rep_len
= jsstr_length(rep_jsstr
);
832 if(SUCCEEDED(hres
)) {
833 const WCHAR
*ecp
= str
;
837 hres
= regexp_match_next(ctx
, regexp
, re_flags
, jsstr
, &match
);
838 re_flags
= (re_flags
| REM_CHECK_GLOBAL
) & (~REM_ALLOC_RESULT
);
840 if(hres
== S_FALSE
) {
847 last_match
.cp
= match
->cp
;
848 last_match
.match_len
= match
->match_len
;
850 if(re_flags
& REM_ALLOC_RESULT
) {
851 re_flags
&= ~REM_ALLOC_RESULT
;
856 match
->cp
= wcsstr(match
->cp
, match_str
);
859 match
->match_len
= jsstr_length(match_jsstr
);
860 match
->cp
+= match
->match_len
;
863 hres
= strbuf_append(&ret
, ecp
, match
->cp
-ecp
-match
->match_len
);
871 hres
= rep_call(ctx
, rep_func
, jsstr
, str
, match
, &cstr
);
875 hres
= strbuf_append_jsstr(&ret
, cstr
);
879 }else if(rep_str
&& regexp
) {
880 const WCHAR
*ptr
= rep_str
, *ptr2
;
882 while((ptr2
= wcschr(ptr
, '$'))) {
883 hres
= strbuf_append(&ret
, ptr
, ptr2
-ptr
);
889 hres
= strbuf_append(&ret
, ptr2
, 1);
893 hres
= strbuf_append(&ret
, match
->cp
-match
->match_len
, match
->match_len
);
897 hres
= strbuf_append(&ret
, str
, match
->cp
-str
-match
->match_len
);
901 hres
= strbuf_append(&ret
, ecp
, (str
+jsstr_length(jsstr
))-ecp
);
907 if(!is_digit(ptr2
[1])) {
908 hres
= strbuf_append(&ret
, ptr2
, 1);
914 if(is_digit(ptr2
[2]) && idx
*10 + (ptr2
[2]-'0') <= match
->paren_count
) {
915 idx
= idx
*10 + (ptr
[2]-'0');
917 }else if(idx
&& idx
<= match
->paren_count
) {
920 hres
= strbuf_append(&ret
, ptr2
, 1);
925 if(match
->parens
[idx
-1].index
!= -1)
926 hres
= strbuf_append(&ret
, str
+match
->parens
[idx
-1].index
,
927 match
->parens
[idx
-1].length
);
936 hres
= strbuf_append(&ret
, ptr
, (rep_str
+rep_len
)-ptr
);
940 hres
= strbuf_append(&ret
, rep_str
, rep_len
);
944 static const WCHAR undefinedW
[] = {'u','n','d','e','f','i','n','e','d'};
946 hres
= strbuf_append(&ret
, undefinedW
, ARRAY_SIZE(undefinedW
));
953 else if(!match
->match_len
)
958 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
962 jsdisp_release(rep_func
);
964 jsstr_release(rep_jsstr
);
966 jsstr_release(match_jsstr
);
970 if(SUCCEEDED(hres
) && last_match
.cp
&& regexp
) {
971 jsstr_release(ctx
->last_match
);
972 ctx
->last_match
= jsstr_addref(jsstr
);
973 ctx
->last_match_index
= last_match
.cp
-str
-last_match
.match_len
;
974 ctx
->last_match_length
= last_match
.match_len
;
978 jsdisp_release(regexp
);
979 jsstr_release(jsstr
);
981 if(SUCCEEDED(hres
) && r
) {
984 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
986 return E_OUTOFMEMORY
;
988 TRACE("= %s\n", debugstr_jsstr(ret_str
));
989 *r
= jsval_string(ret_str
);
996 static HRESULT
String_search(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
999 jsdisp_t
*regexp
= NULL
;
1002 match_state_t match
, *match_ptr
= &match
;
1007 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1014 jsstr_release(jsstr
);
1018 if(is_object_instance(argv
[0])) {
1019 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1020 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
1021 jsdisp_release(regexp
);
1027 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
1029 jsstr_release(jsstr
);
1035 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
1036 jsstr_release(jsstr
);
1037 jsdisp_release(regexp
);
1042 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
1046 /* ECMA-262 3rd Edition 15.5.4.13 */
1047 static HRESULT
String_slice(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1050 int start
=0, end
, length
;
1057 hres
= get_string_val(ctx
, jsthis
, &str
);
1061 length
= jsstr_length(str
);
1063 hres
= to_integer(ctx
, argv
[0], &d
);
1072 start
= length
+ start
;
1075 }else if(start
> length
) {
1084 hres
= to_integer(ctx
, argv
[1], &d
);
1096 }else if(end
> length
) {
1100 end
= d
< 0.0 ? 0 : length
;
1110 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1113 return E_OUTOFMEMORY
;
1116 *r
= jsval_string(retstr
);
1123 static HRESULT
String_small(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1126 static const WCHAR smalltagW
[] = {'S','M','A','L','L',0};
1127 return do_attributeless_tag_format(ctx
, jsthis
, r
, smalltagW
);
1130 static HRESULT
String_split(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1133 match_state_t match_result
, *match_ptr
= &match_result
;
1134 size_t length
, i
= 0, match_len
= 0;
1135 const WCHAR
*ptr
, *ptr2
, *str
, *match_str
= NULL
;
1136 unsigned limit
= ~0u;
1137 jsdisp_t
*array
, *regexp
= NULL
;
1138 jsstr_t
*jsstr
, *match_jsstr
, *tmp_str
;
1141 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1144 length
= jsstr_length(jsstr
);
1146 TRACE("%s\n", debugstr_wn(str
, length
));
1148 if(!argc
|| (is_undefined(argv
[0]) && ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
)) {
1152 hres
= create_array(ctx
, 0, &array
);
1156 /* NOTE: according to spec, we should respect limit argument here (if provided).
1157 * We have a test showing that it's broken in native IE. */
1158 hres
= jsdisp_propput_idx(array
, 0, jsval_string(jsstr
));
1160 jsdisp_release(array
);
1164 *r
= jsval_obj(array
);
1168 if(argc
> 1 && !is_undefined(argv
[1])) {
1169 hres
= to_uint32(ctx
, argv
[1], &limit
);
1171 jsstr_release(jsstr
);
1176 if(is_object_instance(argv
[0])) {
1177 regexp
= iface_to_jsdisp(get_object(argv
[0]));
1179 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1180 jsdisp_release(regexp
);
1187 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1189 jsstr_release(jsstr
);
1193 match_len
= jsstr_length(match_jsstr
);
1195 jsstr_release(match_jsstr
);
1200 hres
= create_array(ctx
, 0, &array
);
1202 if(SUCCEEDED(hres
)) {
1204 match_result
.cp
= str
;
1207 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1210 TRACE("got match %d %d\n", (int)(match_result
.cp
- match_result
.match_len
- str
), match_result
.match_len
);
1211 if(!match_result
.match_len
) {
1212 /* If an empty string is matched, prevent including any match in the result */
1217 if(match_result
.cp
== ptr
) {
1219 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1222 TRACE("retried, got match %d %d\n", (int)(match_result
.cp
- match_result
.match_len
- str
),
1223 match_result
.match_len
);
1225 if(!match_result
.match_len
&& match_result
.cp
== str
+ length
)
1228 ptr2
= match_result
.cp
- match_result
.match_len
;
1229 }else if(match_str
) {
1230 ptr2
= wcsstr(ptr
, match_str
);
1239 if(!regexp
|| ptr2
> ptr
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1240 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1242 hres
= E_OUTOFMEMORY
;
1246 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(tmp_str
));
1247 jsstr_release(tmp_str
);
1253 ptr
= match_result
.cp
;
1255 ptr
= ptr2
+ match_len
;
1261 if(SUCCEEDED(hres
) && (match_str
|| regexp
) && i
<limit
) {
1262 DWORD len
= (str
+length
) - ptr
;
1264 if(len
|| match_str
|| !length
|| ctx
->version
>= SCRIPTLANGUAGEVERSION_ES5
) {
1265 tmp_str
= jsstr_alloc_len(ptr
, len
);
1268 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1269 jsstr_release(tmp_str
);
1271 hres
= E_OUTOFMEMORY
;
1277 jsdisp_release(regexp
);
1279 jsstr_release(match_jsstr
);
1280 jsstr_release(jsstr
);
1282 if(SUCCEEDED(hres
) && r
)
1283 *r
= jsval_obj(array
);
1285 jsdisp_release(array
);
1290 static HRESULT
String_strike(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1293 static const WCHAR striketagW
[] = {'S','T','R','I','K','E',0};
1294 return do_attributeless_tag_format(ctx
, jsthis
, r
, striketagW
);
1297 static HRESULT
String_sub(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1300 static const WCHAR subtagW
[] = {'S','U','B',0};
1301 return do_attributeless_tag_format(ctx
, jsthis
, r
, subtagW
);
1304 /* ECMA-262 3rd Edition 15.5.4.15 */
1305 static HRESULT
String_substring(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1308 INT start
=0, end
, length
;
1315 hres
= get_string_val(ctx
, jsthis
, &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 end
= is_int32(d
) ? min(length
, d
) : length
;
1353 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1355 *r
= jsval_string(ret
);
1357 hres
= E_OUTOFMEMORY
;
1363 /* ECMA-262 3rd Edition B.2.3 */
1364 static HRESULT
String_substr(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1367 int start
=0, len
, length
;
1374 hres
= get_string_val(ctx
, jsthis
, &str
);
1378 length
= jsstr_length(str
);
1380 hres
= to_integer(ctx
, argv
[0], &d
);
1387 start
= is_int32(d
) ? min(length
, d
) : length
;
1391 hres
= to_integer(ctx
, argv
[1], &d
);
1398 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1407 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1409 *r
= jsval_string(ret
);
1411 hres
= E_OUTOFMEMORY
;
1418 static HRESULT
String_sup(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1421 static const WCHAR suptagW
[] = {'S','U','P',0};
1422 return do_attributeless_tag_format(ctx
, jsthis
, r
, suptagW
);
1425 static HRESULT
String_toLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1433 hres
= get_string_val(ctx
, jsthis
, &str
);
1438 unsigned len
= jsstr_length(str
);
1442 ret
= jsstr_alloc_buf(len
, &buf
);
1445 return E_OUTOFMEMORY
;
1448 jsstr_flush(str
, buf
);
1449 for (; len
--; buf
++) *buf
= towlower(*buf
);
1451 *r
= jsval_string(ret
);
1457 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1465 hres
= get_string_val(ctx
, jsthis
, &str
);
1470 unsigned len
= jsstr_length(str
);
1474 ret
= jsstr_alloc_buf(len
, &buf
);
1477 return E_OUTOFMEMORY
;
1480 jsstr_flush(str
, buf
);
1481 for (; len
--; buf
++) *buf
= towupper(*buf
);
1483 *r
= jsval_string(ret
);
1489 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1496 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1503 static HRESULT
String_trim(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
,
1504 jsval_t
*argv
, jsval_t
*r
)
1506 const WCHAR
*str
, *begin
, *end
;
1511 hres
= to_flat_string(ctx
, jsval_disp(jsthis
->u
.disp
), &jsstr
, &str
);
1513 WARN("to_flat_string failed: %08x\n", hres
);
1516 len
= jsstr_length(jsstr
);
1517 TRACE("%s\n", debugstr_wn(str
, len
));
1519 for(begin
= str
, end
= str
+ len
; begin
< end
&& iswspace(*begin
); begin
++);
1520 while(end
> begin
+ 1 && iswspace(*(end
-1))) end
--;
1525 if(begin
== str
&& end
== str
+ len
)
1526 ret
= jsstr_addref(jsstr
);
1528 ret
= jsstr_alloc_len(begin
, end
- begin
);
1530 *r
= jsval_string(ret
);
1532 hres
= E_OUTOFMEMORY
;
1534 jsstr_release(jsstr
);
1538 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1545 static HRESULT
String_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
1547 StringInstance
*This
= string_from_jsdisp(jsthis
);
1551 *r
= jsval_string(jsstr_addref(This
->str
));
1555 static void String_destructor(jsdisp_t
*dispex
)
1557 StringInstance
*This
= string_from_jsdisp(dispex
);
1559 jsstr_release(This
->str
);
1563 static unsigned String_idx_length(jsdisp_t
*jsdisp
)
1565 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1568 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1569 * Newer jscript.dll versions implement it on string type, not class,
1570 * which is not how it should work according to spec. IE9 implements it
1571 * properly, but it uses its own JavaScript engine inside MSHTML. We
1572 * implement it here, but in the way IE9 and spec work.
1574 return string
->dispex
.ctx
->version
< 2 ? 0 : jsstr_length(string
->str
);
1577 static HRESULT
String_idx_get(jsdisp_t
*jsdisp
, unsigned idx
, jsval_t
*r
)
1579 StringInstance
*string
= string_from_jsdisp(jsdisp
);
1582 ret
= jsstr_substr(string
->str
, idx
, 1);
1584 return E_OUTOFMEMORY
;
1586 TRACE("%p[%u] = %s\n", string
, idx
, debugstr_jsstr(ret
));
1588 *r
= jsval_string(ret
);
1592 static const builtin_prop_t String_props
[] = {
1593 {anchorW
, String_anchor
, PROPF_METHOD
|1},
1594 {bigW
, String_big
, PROPF_METHOD
},
1595 {blinkW
, String_blink
, PROPF_METHOD
},
1596 {boldW
, String_bold
, PROPF_METHOD
},
1597 {charAtW
, String_charAt
, PROPF_METHOD
|1},
1598 {charCodeAtW
, String_charCodeAt
, PROPF_METHOD
|1},
1599 {concatW
, String_concat
, PROPF_METHOD
|1},
1600 {fixedW
, String_fixed
, PROPF_METHOD
},
1601 {fontcolorW
, String_fontcolor
, PROPF_METHOD
|1},
1602 {fontsizeW
, String_fontsize
, PROPF_METHOD
|1},
1603 {indexOfW
, String_indexOf
, PROPF_METHOD
|2},
1604 {italicsW
, String_italics
, PROPF_METHOD
},
1605 {lastIndexOfW
, String_lastIndexOf
, PROPF_METHOD
|2},
1606 {lengthW
, NULL
,0, String_get_length
},
1607 {linkW
, String_link
, PROPF_METHOD
|1},
1608 {localeCompareW
, String_localeCompare
, PROPF_METHOD
|1},
1609 {matchW
, String_match
, PROPF_METHOD
|1},
1610 {replaceW
, String_replace
, PROPF_METHOD
|1},
1611 {searchW
, String_search
, PROPF_METHOD
},
1612 {sliceW
, String_slice
, PROPF_METHOD
},
1613 {smallW
, String_small
, PROPF_METHOD
},
1614 {splitW
, String_split
, PROPF_METHOD
|2},
1615 {strikeW
, String_strike
, PROPF_METHOD
},
1616 {subW
, String_sub
, PROPF_METHOD
},
1617 {substrW
, String_substr
, PROPF_METHOD
|2},
1618 {substringW
, String_substring
, PROPF_METHOD
|2},
1619 {supW
, String_sup
, PROPF_METHOD
},
1620 {toLocaleLowerCaseW
, String_toLocaleLowerCase
, PROPF_METHOD
},
1621 {toLocaleUpperCaseW
, String_toLocaleUpperCase
, PROPF_METHOD
},
1622 {toLowerCaseW
, String_toLowerCase
, PROPF_METHOD
},
1623 {toStringW
, String_toString
, PROPF_METHOD
},
1624 {toUpperCaseW
, String_toUpperCase
, PROPF_METHOD
},
1625 {trimW
, String_trim
, PROPF_ES5
|PROPF_METHOD
},
1626 {valueOfW
, String_valueOf
, PROPF_METHOD
}
1629 static const builtin_info_t String_info
= {
1631 {NULL
, NULL
,0, String_get_value
},
1632 ARRAY_SIZE(String_props
),
1638 static const builtin_prop_t StringInst_props
[] = {
1639 {lengthW
, NULL
,0, String_get_length
}
1642 static const builtin_info_t StringInst_info
= {
1644 {NULL
, NULL
,0, String_get_value
},
1645 ARRAY_SIZE(StringInst_props
),
1653 /* ECMA-262 3rd Edition 15.5.3.2 */
1654 static HRESULT
StringConstr_fromCharCode(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
1655 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1664 ret
= jsstr_alloc_buf(argc
, &ret_str
);
1666 return E_OUTOFMEMORY
;
1668 for(i
=0; i
<argc
; i
++) {
1669 hres
= to_uint32(ctx
, argv
[i
], &code
);
1679 *r
= jsval_string(ret
);
1685 static HRESULT
StringConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1697 hres
= to_string(ctx
, argv
[0], &str
);
1701 str
= jsstr_empty();
1704 *r
= jsval_string(str
);
1707 case DISPATCH_CONSTRUCT
: {
1712 hres
= to_string(ctx
, argv
[0], &str
);
1716 str
= jsstr_empty();
1719 hres
= create_string(ctx
, str
, &ret
);
1720 if (SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1726 FIXME("unimplemented flags: %x\n", flags
);
1733 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1735 StringInstance
*string
;
1738 string
= heap_alloc_zero(sizeof(StringInstance
));
1740 return E_OUTOFMEMORY
;
1742 if(object_prototype
)
1743 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1745 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1751 string
->str
= jsstr_addref(str
);
1756 static const builtin_prop_t StringConstr_props
[] = {
1757 {fromCharCodeW
, StringConstr_fromCharCode
, PROPF_METHOD
},
1760 static const builtin_info_t StringConstr_info
= {
1762 DEFAULT_FUNCTION_VALUE
,
1763 ARRAY_SIZE(StringConstr_props
),
1769 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1771 StringInstance
*string
;
1774 static const WCHAR StringW
[] = {'S','t','r','i','n','g',0};
1776 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1780 hres
= create_builtin_constructor(ctx
, StringConstr_value
, StringW
, &StringConstr_info
,
1781 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1783 jsdisp_release(&string
->dispex
);
1787 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1789 StringInstance
*string
;
1792 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1796 *ret
= &string
->dispex
;