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
20 #include "wine/port.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
29 #define UINT32_MAX 0xffffffff
36 static const WCHAR lengthW
[] = {'l','e','n','g','t','h',0};
37 static const WCHAR toStringW
[] = {'t','o','S','t','r','i','n','g',0};
38 static const WCHAR valueOfW
[] = {'v','a','l','u','e','O','f',0};
39 static const WCHAR anchorW
[] = {'a','n','c','h','o','r',0};
40 static const WCHAR bigW
[] = {'b','i','g',0};
41 static const WCHAR blinkW
[] = {'b','l','i','n','k',0};
42 static const WCHAR boldW
[] = {'b','o','l','d',0};
43 static const WCHAR charAtW
[] = {'c','h','a','r','A','t',0};
44 static const WCHAR charCodeAtW
[] = {'c','h','a','r','C','o','d','e','A','t',0};
45 static const WCHAR concatW
[] = {'c','o','n','c','a','t',0};
46 static const WCHAR fixedW
[] = {'f','i','x','e','d',0};
47 static const WCHAR fontcolorW
[] = {'f','o','n','t','c','o','l','o','r',0};
48 static const WCHAR fontsizeW
[] = {'f','o','n','t','s','i','z','e',0};
49 static const WCHAR indexOfW
[] = {'i','n','d','e','x','O','f',0};
50 static const WCHAR italicsW
[] = {'i','t','a','l','i','c','s',0};
51 static const WCHAR lastIndexOfW
[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
52 static const WCHAR linkW
[] = {'l','i','n','k',0};
53 static const WCHAR matchW
[] = {'m','a','t','c','h',0};
54 static const WCHAR replaceW
[] = {'r','e','p','l','a','c','e',0};
55 static const WCHAR searchW
[] = {'s','e','a','r','c','h',0};
56 static const WCHAR sliceW
[] = {'s','l','i','c','e',0};
57 static const WCHAR smallW
[] = {'s','m','a','l','l',0};
58 static const WCHAR splitW
[] = {'s','p','l','i','t',0};
59 static const WCHAR strikeW
[] = {'s','t','r','i','k','e',0};
60 static const WCHAR subW
[] = {'s','u','b',0};
61 static const WCHAR substringW
[] = {'s','u','b','s','t','r','i','n','g',0};
62 static const WCHAR substrW
[] = {'s','u','b','s','t','r',0};
63 static const WCHAR supW
[] = {'s','u','p',0};
64 static const WCHAR toLowerCaseW
[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
65 static const WCHAR toUpperCaseW
[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
66 static const WCHAR toLocaleLowerCaseW
[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
67 static const WCHAR toLocaleUpperCaseW
[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
68 static const WCHAR localeCompareW
[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
69 static const WCHAR fromCharCodeW
[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
71 static inline StringInstance
*string_from_vdisp(vdisp_t
*vdisp
)
73 return (StringInstance
*)vdisp
->u
.jsdisp
;
76 static inline StringInstance
*string_this(vdisp_t
*jsthis
)
78 return is_vclass(jsthis
, JSCLASS_STRING
) ? string_from_vdisp(jsthis
) : NULL
;
81 static HRESULT
get_string_val(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsstr_t
**val
)
83 StringInstance
*string
;
85 if((string
= string_this(jsthis
))) {
86 *val
= jsstr_addref(string
->str
);
90 return to_string(ctx
, jsval_disp(jsthis
->u
.disp
), val
);
93 static HRESULT
get_string_flat_val(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsstr_t
**jsval
, const WCHAR
**val
)
97 hres
= get_string_val(ctx
, jsthis
, jsval
);
101 *val
= jsstr_flatten(*jsval
);
105 jsstr_release(*jsval
);
106 return E_OUTOFMEMORY
;
109 static HRESULT
String_length(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
112 TRACE("%p\n", jsthis
);
115 case DISPATCH_PROPERTYGET
: {
116 StringInstance
*string
= string_from_vdisp(jsthis
);
118 *r
= jsval_number(jsstr_length(string
->str
));
122 FIXME("unimplemented flags %x\n", flags
);
129 static HRESULT
stringobj_to_string(vdisp_t
*jsthis
, jsval_t
*r
)
131 StringInstance
*string
;
133 if(!(string
= string_this(jsthis
))) {
134 WARN("this is not a string object\n");
139 *r
= jsval_string(jsstr_addref(string
->str
));
143 /* ECMA-262 3rd Edition 15.5.4.2 */
144 static HRESULT
String_toString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
149 return stringobj_to_string(jsthis
, r
);
152 /* ECMA-262 3rd Edition 15.5.4.2 */
153 static HRESULT
String_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
158 return stringobj_to_string(jsthis
, r
);
161 static HRESULT
do_attributeless_tag_format(script_ctx_t
*ctx
, vdisp_t
*jsthis
, jsval_t
*r
, const WCHAR
*tagname
)
163 unsigned tagname_len
;
168 hres
= get_string_val(ctx
, jsthis
, &str
);
177 tagname_len
= strlenW(tagname
);
179 ptr
= jsstr_alloc_buf(jsstr_length(str
) + 2*tagname_len
+ 5, &ret
);
182 return E_OUTOFMEMORY
;
186 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
190 ptr
+= jsstr_flush(str
, ptr
);
195 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
199 *r
= jsval_string(ret
);
203 static HRESULT
do_attribute_tag_format(script_ctx_t
*ctx
, vdisp_t
*jsthis
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
,
204 const WCHAR
*tagname
, const WCHAR
*attrname
)
206 jsstr_t
*str
, *attr_value
= NULL
;
209 hres
= get_string_val(ctx
, jsthis
, &str
);
214 hres
= to_string(ctx
, argv
[0], &attr_value
);
220 attr_value
= jsstr_undefined();
224 unsigned attrname_len
= strlenW(attrname
);
225 unsigned tagname_len
= strlenW(tagname
);
229 ptr
= jsstr_alloc_buf(2*tagname_len
+ attrname_len
+ jsstr_length(attr_value
) + jsstr_length(str
) + 9, &ret
);
232 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
235 memcpy(ptr
, attrname
, attrname_len
*sizeof(WCHAR
));
239 ptr
+= jsstr_flush(attr_value
, ptr
);
242 ptr
+= jsstr_flush(str
, ptr
);
246 memcpy(ptr
, tagname
, tagname_len
*sizeof(WCHAR
));
250 *r
= jsval_string(ret
);
252 hres
= E_OUTOFMEMORY
;
256 jsstr_release(attr_value
);
261 static HRESULT
String_anchor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
264 static const WCHAR fontW
[] = {'A',0};
265 static const WCHAR colorW
[] = {'N','A','M','E',0};
267 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
270 static HRESULT
String_big(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
273 static const WCHAR bigtagW
[] = {'B','I','G',0};
274 return do_attributeless_tag_format(ctx
, jsthis
, r
, bigtagW
);
277 static HRESULT
String_blink(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
280 static const WCHAR blinktagW
[] = {'B','L','I','N','K',0};
281 return do_attributeless_tag_format(ctx
, jsthis
, r
, blinktagW
);
284 static HRESULT
String_bold(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
287 static const WCHAR boldtagW
[] = {'B',0};
288 return do_attributeless_tag_format(ctx
, jsthis
, r
, boldtagW
);
291 /* ECMA-262 3rd Edition 15.5.4.5 */
292 static HRESULT
String_charAt(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
301 hres
= get_string_val(ctx
, jsthis
, &str
);
308 hres
= to_integer(ctx
, argv
[0], &d
);
313 pos
= is_int32(d
) ? d
: -1;
321 if(0 <= pos
&& pos
< jsstr_length(str
)) {
322 ret
= jsstr_substr(str
, pos
, 1);
324 return E_OUTOFMEMORY
;
329 *r
= jsval_string(ret
);
333 /* ECMA-262 3rd Edition 15.5.4.5 */
334 static HRESULT
String_charCodeAt(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
343 hres
= get_string_val(ctx
, jsthis
, &str
);
350 hres
= to_integer(ctx
, argv
[0], &d
);
356 if(!is_int32(d
) || d
< 0 || d
>= jsstr_length(str
)) {
359 *r
= jsval_number(NAN
);
368 jsstr_extract(str
, idx
, 1, &c
);
369 *r
= jsval_number(c
);
376 /* ECMA-262 3rd Edition 15.5.4.6 */
377 static HRESULT
String_concat(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
385 hres
= get_string_val(ctx
, jsthis
, &str
);
396 hres
= to_string(ctx
, argv
[0], &arg_str
);
402 ret
= jsstr_concat(str
, arg_str
);
405 return E_OUTOFMEMORY
;
409 const unsigned str_cnt
= argc
+1;
414 strs
= heap_alloc_zero(str_cnt
* sizeof(*strs
));
417 return E_OUTOFMEMORY
;
421 for(i
=0; i
< argc
; i
++) {
422 hres
= to_string(ctx
, argv
[i
], strs
+i
+1);
427 if(SUCCEEDED(hres
)) {
428 for(i
=0; i
< str_cnt
; i
++) {
429 len
+= jsstr_length(strs
[i
]);
430 if(len
> JSSTR_MAX_LENGTH
) {
431 hres
= E_OUTOFMEMORY
;
436 if(SUCCEEDED(hres
)) {
437 ptr
= jsstr_alloc_buf(len
, &ret
);
439 for(i
=0; i
< str_cnt
; i
++)
440 ptr
+= jsstr_flush(strs
[i
], ptr
);
442 hres
= E_OUTOFMEMORY
;
448 jsstr_release(strs
[i
]);
456 *r
= jsval_string(ret
);
462 static HRESULT
String_fixed(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
465 static const WCHAR fixedtagW
[] = {'T','T',0};
466 return do_attributeless_tag_format(ctx
, jsthis
, r
, fixedtagW
);
469 static HRESULT
String_fontcolor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
472 static const WCHAR fontW
[] = {'F','O','N','T',0};
473 static const WCHAR colorW
[] = {'C','O','L','O','R',0};
475 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
478 static HRESULT
String_fontsize(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
481 static const WCHAR fontW
[] = {'F','O','N','T',0};
482 static const WCHAR colorW
[] = {'S','I','Z','E',0};
484 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
487 static HRESULT
String_indexOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
490 jsstr_t
*search_jsstr
, *jsstr
;
491 const WCHAR
*search_str
, *str
;
498 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
502 length
= jsstr_length(jsstr
);
505 *r
= jsval_number(-1);
506 jsstr_release(jsstr
);
510 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
512 jsstr_release(jsstr
);
519 hres
= to_integer(ctx
, argv
[1], &d
);
520 if(SUCCEEDED(hres
) && d
> 0.0)
521 pos
= is_int32(d
) ? min(length
, d
) : length
;
524 if(SUCCEEDED(hres
)) {
527 ptr
= strstrW(str
+pos
, search_str
);
534 jsstr_release(search_jsstr
);
535 jsstr_release(jsstr
);
540 *r
= jsval_number(ret
);
544 static HRESULT
String_italics(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
547 static const WCHAR italicstagW
[] = {'I',0};
548 return do_attributeless_tag_format(ctx
, jsthis
, r
, italicstagW
);
551 /* ECMA-262 3rd Edition 15.5.4.8 */
552 static HRESULT
String_lastIndexOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
555 unsigned pos
= 0, search_len
, length
;
556 jsstr_t
*search_jsstr
, *jsstr
;
557 const WCHAR
*search_str
, *str
;
563 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
569 *r
= jsval_number(-1);
570 jsstr_release(jsstr
);
574 hres
= to_flat_string(ctx
, argv
[0], &search_jsstr
, &search_str
);
576 jsstr_release(jsstr
);
580 search_len
= jsstr_length(search_jsstr
);
581 length
= jsstr_length(jsstr
);
586 hres
= to_integer(ctx
, argv
[1], &d
);
587 if(SUCCEEDED(hres
) && d
> 0)
588 pos
= is_int32(d
) ? min(length
, d
) : length
;
593 if(SUCCEEDED(hres
) && length
>= search_len
) {
596 for(ptr
= str
+min(pos
, length
-search_len
); ptr
>= str
; ptr
--) {
597 if(!memcmp(ptr
, search_str
, search_len
*sizeof(WCHAR
))) {
604 jsstr_release(search_jsstr
);
605 jsstr_release(jsstr
);
610 *r
= jsval_number(ret
);
614 static HRESULT
String_link(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
617 static const WCHAR fontW
[] = {'A',0};
618 static const WCHAR colorW
[] = {'H','R','E','F',0};
620 return do_attribute_tag_format(ctx
, jsthis
, argc
, argv
, r
, fontW
, colorW
);
623 /* ECMA-262 3rd Edition 15.5.4.10 */
624 static HRESULT
String_match(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
627 jsdisp_t
*regexp
= NULL
;
639 if(is_object_instance(argv
[0])) {
640 regexp
= iface_to_jsdisp((IUnknown
*)get_object(argv
[0]));
641 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
642 jsdisp_release(regexp
);
650 hres
= to_string(ctx
, argv
[0], &match_str
);
654 hres
= create_regexp(ctx
, match_str
, 0, ®exp
);
655 jsstr_release(match_str
);
660 hres
= get_string_val(ctx
, jsthis
, &str
);
662 hres
= regexp_string_match(ctx
, regexp
, str
, r
);
664 jsdisp_release(regexp
);
675 static BOOL
strbuf_ensure_size(strbuf_t
*buf
, unsigned len
)
683 new_size
= buf
->size
? buf
->size
<<1 : 16;
687 new_buf
= heap_realloc(buf
->buf
, new_size
*sizeof(WCHAR
));
689 new_buf
= heap_alloc(new_size
*sizeof(WCHAR
));
694 buf
->size
= new_size
;
698 static HRESULT
strbuf_append(strbuf_t
*buf
, const WCHAR
*str
, DWORD len
)
703 if(!strbuf_ensure_size(buf
, buf
->len
+len
))
704 return E_OUTOFMEMORY
;
706 memcpy(buf
->buf
+buf
->len
, str
, len
*sizeof(WCHAR
));
711 static HRESULT
strbuf_append_jsstr(strbuf_t
*buf
, jsstr_t
*str
)
713 if(!strbuf_ensure_size(buf
, buf
->len
+jsstr_length(str
)))
714 return E_OUTOFMEMORY
;
716 jsstr_flush(str
, buf
->buf
+buf
->len
);
717 buf
->len
+= jsstr_length(str
);
721 static HRESULT
rep_call(script_ctx_t
*ctx
, jsdisp_t
*func
,
722 jsstr_t
*jsstr
, const WCHAR
*str
, match_state_t
*match
, jsstr_t
**ret
)
731 argc
= match
->paren_count
+3;
732 argv
= heap_alloc_zero(sizeof(*argv
)*argc
);
734 return E_OUTOFMEMORY
;
736 tmp_str
= jsstr_alloc_len(match
->cp
-match
->match_len
, match
->match_len
);
738 hres
= E_OUTOFMEMORY
;
739 argv
[0] = jsval_string(tmp_str
);
741 if(SUCCEEDED(hres
)) {
742 for(i
=0; i
< match
->paren_count
; i
++) {
743 if(match
->parens
[i
].index
!= -1)
744 tmp_str
= jsstr_substr(jsstr
, match
->parens
[i
].index
, match
->parens
[i
].length
);
746 tmp_str
= jsstr_empty();
748 hres
= E_OUTOFMEMORY
;
751 argv
[i
+1] = jsval_string(tmp_str
);
755 if(SUCCEEDED(hres
)) {
756 argv
[match
->paren_count
+1] = jsval_number(match
->cp
-str
- match
->match_len
);
757 argv
[match
->paren_count
+2] = jsval_string(jsstr
);
761 hres
= jsdisp_call_value(func
, NULL
, DISPATCH_METHOD
, argc
, argv
, &val
);
763 for(i
=0; i
<= match
->paren_count
; i
++)
764 jsstr_release(get_string(argv
[i
]));
770 hres
= to_string(ctx
, val
, ret
);
775 /* ECMA-262 3rd Edition 15.5.4.11 */
776 static HRESULT
String_replace(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
779 const WCHAR
*str
, *match_str
= NULL
, *rep_str
= NULL
;
780 jsstr_t
*rep_jsstr
, *match_jsstr
, *jsstr
;
781 jsdisp_t
*rep_func
= NULL
, *regexp
= NULL
;
782 match_state_t
*match
= NULL
, last_match
= {0};
783 strbuf_t ret
= {NULL
,0,0};
784 DWORD re_flags
= REM_NO_CTX_UPDATE
|REM_ALLOC_RESULT
;
790 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
796 *r
= jsval_string(jsstr
);
798 jsstr_release(jsstr
);
802 if(is_object_instance(argv
[0])) {
803 regexp
= iface_to_jsdisp((IUnknown
*)get_object(argv
[0]));
804 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
805 jsdisp_release(regexp
);
811 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
813 jsstr_release(jsstr
);
819 if(is_object_instance(argv
[1])) {
820 rep_func
= iface_to_jsdisp((IUnknown
*)get_object(argv
[1]));
821 if(rep_func
&& !is_class(rep_func
, JSCLASS_FUNCTION
)) {
822 jsdisp_release(rep_func
);
828 hres
= to_flat_string(ctx
, argv
[1], &rep_jsstr
, &rep_str
);
830 rep_len
= jsstr_length(rep_jsstr
);
834 if(SUCCEEDED(hres
)) {
835 const WCHAR
*ecp
= str
;
839 hres
= regexp_match_next(ctx
, regexp
, re_flags
, jsstr
, &match
);
840 re_flags
= (re_flags
| REM_CHECK_GLOBAL
) & (~REM_ALLOC_RESULT
);
842 if(hres
== S_FALSE
) {
849 last_match
.cp
= match
->cp
;
850 last_match
.match_len
= match
->match_len
;
852 if(re_flags
& REM_ALLOC_RESULT
) {
853 re_flags
&= ~REM_ALLOC_RESULT
;
858 match
->cp
= strstrW(match
->cp
, match_str
);
861 match
->match_len
= jsstr_length(match_jsstr
);
862 match
->cp
+= match
->match_len
;
865 hres
= strbuf_append(&ret
, ecp
, match
->cp
-ecp
-match
->match_len
);
873 hres
= rep_call(ctx
, rep_func
, jsstr
, str
, match
, &cstr
);
877 hres
= strbuf_append_jsstr(&ret
, cstr
);
881 }else if(rep_str
&& regexp
) {
882 const WCHAR
*ptr
= rep_str
, *ptr2
;
884 while((ptr2
= strchrW(ptr
, '$'))) {
885 hres
= strbuf_append(&ret
, ptr
, ptr2
-ptr
);
891 hres
= strbuf_append(&ret
, ptr2
, 1);
895 hres
= strbuf_append(&ret
, match
->cp
-match
->match_len
, match
->match_len
);
899 hres
= strbuf_append(&ret
, str
, match
->cp
-str
-match
->match_len
);
903 hres
= strbuf_append(&ret
, ecp
, (str
+jsstr_length(jsstr
))-ecp
);
909 if(!isdigitW(ptr2
[1])) {
910 hres
= strbuf_append(&ret
, ptr2
, 1);
916 if(isdigitW(ptr2
[2]) && idx
*10 + (ptr2
[2]-'0') <= match
->paren_count
) {
917 idx
= idx
*10 + (ptr
[2]-'0');
919 }else if(idx
&& idx
<= match
->paren_count
) {
922 hres
= strbuf_append(&ret
, ptr2
, 1);
927 if(match
->parens
[idx
-1].index
!= -1)
928 hres
= strbuf_append(&ret
, str
+match
->parens
[idx
-1].index
,
929 match
->parens
[idx
-1].length
);
938 hres
= strbuf_append(&ret
, ptr
, (rep_str
+rep_len
)-ptr
);
942 hres
= strbuf_append(&ret
, rep_str
, rep_len
);
946 static const WCHAR undefinedW
[] = {'u','n','d','e','f','i','n','e','d'};
948 hres
= strbuf_append(&ret
, undefinedW
, sizeof(undefinedW
)/sizeof(WCHAR
));
955 else if(!match
->match_len
)
960 hres
= strbuf_append(&ret
, ecp
, str
+jsstr_length(jsstr
)-ecp
);
964 jsdisp_release(rep_func
);
966 jsstr_release(rep_jsstr
);
968 jsstr_release(match_jsstr
);
972 if(SUCCEEDED(hres
) && last_match
.cp
&& regexp
) {
973 jsstr_release(ctx
->last_match
);
974 ctx
->last_match
= jsstr_addref(jsstr
);
975 ctx
->last_match_index
= last_match
.cp
-str
-last_match
.match_len
;
976 ctx
->last_match_length
= last_match
.match_len
;
980 jsdisp_release(regexp
);
981 jsstr_release(jsstr
);
983 if(SUCCEEDED(hres
) && r
) {
986 ret_str
= jsstr_alloc_len(ret
.buf
, ret
.len
);
988 return E_OUTOFMEMORY
;
990 TRACE("= %s\n", debugstr_jsstr(ret_str
));
991 *r
= jsval_string(ret_str
);
998 static HRESULT
String_search(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1001 jsdisp_t
*regexp
= NULL
;
1004 match_state_t match
, *match_ptr
= &match
;
1009 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1016 jsstr_release(jsstr
);
1020 if(is_object_instance(argv
[0])) {
1021 regexp
= iface_to_jsdisp((IUnknown
*)get_object(argv
[0]));
1022 if(regexp
&& !is_class(regexp
, JSCLASS_REGEXP
)) {
1023 jsdisp_release(regexp
);
1029 hres
= create_regexp_var(ctx
, argv
[0], NULL
, ®exp
);
1031 jsstr_release(jsstr
);
1037 hres
= regexp_match_next(ctx
, regexp
, REM_RESET_INDEX
|REM_NO_PARENS
, jsstr
, &match_ptr
);
1038 jsstr_release(jsstr
);
1039 jsdisp_release(regexp
);
1044 *r
= jsval_number(hres
== S_OK
? match
.cp
-match
.match_len
-str
: -1);
1048 /* ECMA-262 3rd Edition 15.5.4.13 */
1049 static HRESULT
String_slice(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1052 int start
=0, end
, length
;
1059 hres
= get_string_val(ctx
, jsthis
, &str
);
1063 length
= jsstr_length(str
);
1065 hres
= to_integer(ctx
, argv
[0], &d
);
1074 start
= length
+ start
;
1077 }else if(start
> length
) {
1086 hres
= to_integer(ctx
, argv
[1], &d
);
1098 }else if(end
> length
) {
1102 end
= d
< 0.0 ? 0 : length
;
1112 jsstr_t
*retstr
= jsstr_substr(str
, start
, end
-start
);
1115 return E_OUTOFMEMORY
;
1118 *r
= jsval_string(retstr
);
1125 static HRESULT
String_small(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1128 static const WCHAR smalltagW
[] = {'S','M','A','L','L',0};
1129 return do_attributeless_tag_format(ctx
, jsthis
, r
, smalltagW
);
1132 static HRESULT
String_split(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1135 match_state_t match_result
, *match_ptr
= &match_result
;
1136 DWORD length
, i
, match_len
= 0;
1137 const WCHAR
*ptr
, *ptr2
, *str
, *match_str
= NULL
;
1138 unsigned limit
= UINT32_MAX
;
1139 jsdisp_t
*array
, *regexp
= NULL
;
1140 jsstr_t
*jsstr
, *match_jsstr
, *tmp_str
;
1145 if(argc
!= 1 && argc
!= 2) {
1146 FIXME("unsupported argc %u\n", argc
);
1150 hres
= get_string_flat_val(ctx
, jsthis
, &jsstr
, &str
);
1154 length
= jsstr_length(jsstr
);
1156 if(argc
> 1 && !is_undefined(argv
[1])) {
1157 hres
= to_uint32(ctx
, argv
[1], &limit
);
1159 jsstr_release(jsstr
);
1164 if(is_object_instance(argv
[0])) {
1165 regexp
= iface_to_jsdisp((IUnknown
*)get_object(argv
[0]));
1167 if(!is_class(regexp
, JSCLASS_REGEXP
)) {
1168 jsdisp_release(regexp
);
1175 hres
= to_flat_string(ctx
, argv
[0], &match_jsstr
, &match_str
);
1177 jsstr_release(jsstr
);
1181 match_len
= jsstr_length(match_jsstr
);
1183 jsstr_release(match_jsstr
);
1188 hres
= create_array(ctx
, 0, &array
);
1190 if(SUCCEEDED(hres
)) {
1192 match_result
.cp
= str
;
1193 for(i
=0; i
<limit
; i
++) {
1195 hres
= regexp_match_next(ctx
, regexp
, REM_NO_PARENS
, jsstr
, &match_ptr
);
1198 ptr2
= match_result
.cp
- match_result
.match_len
;
1199 }else if(match_str
) {
1200 ptr2
= strstrW(ptr
, match_str
);
1209 tmp_str
= jsstr_alloc_len(ptr
, ptr2
-ptr
);
1211 hres
= E_OUTOFMEMORY
;
1215 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1216 jsstr_release(tmp_str
);
1221 ptr
= match_result
.cp
;
1223 ptr
= ptr2
+ match_len
;
1229 if(SUCCEEDED(hres
) && (match_str
|| regexp
) && i
<limit
) {
1230 DWORD len
= (str
+length
) - ptr
;
1232 if(len
|| match_str
) {
1233 tmp_str
= jsstr_alloc_len(ptr
, len
);
1236 hres
= jsdisp_propput_idx(array
, i
, jsval_string(tmp_str
));
1237 jsstr_release(tmp_str
);
1239 hres
= E_OUTOFMEMORY
;
1245 jsdisp_release(regexp
);
1247 jsstr_release(match_jsstr
);
1248 jsstr_release(jsstr
);
1250 if(SUCCEEDED(hres
) && r
)
1251 *r
= jsval_obj(array
);
1253 jsdisp_release(array
);
1258 static HRESULT
String_strike(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1261 static const WCHAR striketagW
[] = {'S','T','R','I','K','E',0};
1262 return do_attributeless_tag_format(ctx
, jsthis
, r
, striketagW
);
1265 static HRESULT
String_sub(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1268 static const WCHAR subtagW
[] = {'S','U','B',0};
1269 return do_attributeless_tag_format(ctx
, jsthis
, r
, subtagW
);
1272 /* ECMA-262 3rd Edition 15.5.4.15 */
1273 static HRESULT
String_substring(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1276 INT start
=0, end
, length
;
1283 hres
= get_string_val(ctx
, jsthis
, &str
);
1287 length
= jsstr_length(str
);
1289 hres
= to_integer(ctx
, argv
[0], &d
);
1296 start
= is_int32(d
) ? min(length
, d
) : length
;
1300 hres
= to_integer(ctx
, argv
[1], &d
);
1307 end
= is_int32(d
) ? min(length
, d
) : length
;
1321 jsstr_t
*ret
= jsstr_substr(str
, start
, end
-start
);
1323 *r
= jsval_string(ret
);
1325 hres
= E_OUTOFMEMORY
;
1331 /* ECMA-262 3rd Edition B.2.3 */
1332 static HRESULT
String_substr(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1335 int start
=0, len
, length
;
1342 hres
= get_string_val(ctx
, jsthis
, &str
);
1346 length
= jsstr_length(str
);
1348 hres
= to_integer(ctx
, argv
[0], &d
);
1355 start
= is_int32(d
) ? min(length
, d
) : length
;
1359 hres
= to_integer(ctx
, argv
[1], &d
);
1366 len
= is_int32(d
) ? min(length
-start
, d
) : length
-start
;
1375 jsstr_t
*ret
= jsstr_substr(str
, start
, len
);
1377 *r
= jsval_string(ret
);
1379 hres
= E_OUTOFMEMORY
;
1386 static HRESULT
String_sup(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1389 static const WCHAR suptagW
[] = {'S','U','P',0};
1390 return do_attributeless_tag_format(ctx
, jsthis
, r
, suptagW
);
1393 static HRESULT
String_toLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1401 hres
= get_string_val(ctx
, jsthis
, &str
);
1409 buf
= jsstr_alloc_buf(jsstr_length(str
), &ret
);
1412 return E_OUTOFMEMORY
;
1415 jsstr_flush(str
, buf
);
1417 *r
= jsval_string(ret
);
1423 static HRESULT
String_toUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1431 hres
= get_string_val(ctx
, jsthis
, &str
);
1439 buf
= jsstr_alloc_buf(jsstr_length(str
), &ret
);
1442 return E_OUTOFMEMORY
;
1445 jsstr_flush(str
, buf
);
1447 *r
= jsval_string(ret
);
1453 static HRESULT
String_toLocaleLowerCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1460 static HRESULT
String_toLocaleUpperCase(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1467 static HRESULT
String_localeCompare(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1474 static HRESULT
String_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1477 StringInstance
*This
= string_from_vdisp(jsthis
);
1483 return throw_type_error(ctx
, JS_E_FUNCTION_EXPECTED
, NULL
);
1484 case DISPATCH_PROPERTYGET
: {
1485 *r
= jsval_string(jsstr_addref(This
->str
));
1489 FIXME("flags %x\n", flags
);
1496 static void String_destructor(jsdisp_t
*dispex
)
1498 StringInstance
*This
= (StringInstance
*)dispex
;
1500 jsstr_release(This
->str
);
1504 static unsigned String_idx_length(jsdisp_t
*jsdisp
)
1506 StringInstance
*string
= (StringInstance
*)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
= (StringInstance
*)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 {anchorW
, String_anchor
, PROPF_METHOD
|1},
1535 {bigW
, String_big
, PROPF_METHOD
},
1536 {blinkW
, String_blink
, PROPF_METHOD
},
1537 {boldW
, String_bold
, PROPF_METHOD
},
1538 {charAtW
, String_charAt
, PROPF_METHOD
|1},
1539 {charCodeAtW
, String_charCodeAt
, PROPF_METHOD
|1},
1540 {concatW
, String_concat
, PROPF_METHOD
|1},
1541 {fixedW
, String_fixed
, PROPF_METHOD
},
1542 {fontcolorW
, String_fontcolor
, PROPF_METHOD
|1},
1543 {fontsizeW
, String_fontsize
, PROPF_METHOD
|1},
1544 {indexOfW
, String_indexOf
, PROPF_METHOD
|2},
1545 {italicsW
, String_italics
, PROPF_METHOD
},
1546 {lastIndexOfW
, String_lastIndexOf
, PROPF_METHOD
|2},
1547 {lengthW
, String_length
, 0},
1548 {linkW
, String_link
, PROPF_METHOD
|1},
1549 {localeCompareW
, String_localeCompare
, PROPF_METHOD
|1},
1550 {matchW
, String_match
, PROPF_METHOD
|1},
1551 {replaceW
, String_replace
, PROPF_METHOD
|1},
1552 {searchW
, String_search
, PROPF_METHOD
},
1553 {sliceW
, String_slice
, PROPF_METHOD
},
1554 {smallW
, String_small
, PROPF_METHOD
},
1555 {splitW
, String_split
, PROPF_METHOD
|2},
1556 {strikeW
, String_strike
, PROPF_METHOD
},
1557 {subW
, String_sub
, PROPF_METHOD
},
1558 {substrW
, String_substr
, PROPF_METHOD
|2},
1559 {substringW
, String_substring
, PROPF_METHOD
|2},
1560 {supW
, String_sup
, PROPF_METHOD
},
1561 {toLocaleLowerCaseW
, String_toLocaleLowerCase
, PROPF_METHOD
},
1562 {toLocaleUpperCaseW
, String_toLocaleUpperCase
, PROPF_METHOD
},
1563 {toLowerCaseW
, String_toLowerCase
, PROPF_METHOD
},
1564 {toStringW
, String_toString
, PROPF_METHOD
},
1565 {toUpperCaseW
, String_toUpperCase
, PROPF_METHOD
},
1566 {valueOfW
, String_valueOf
, PROPF_METHOD
}
1569 static const builtin_info_t String_info
= {
1571 {NULL
, String_value
, 0},
1572 sizeof(String_props
)/sizeof(*String_props
),
1578 static const builtin_prop_t StringInst_props
[] = {
1579 {lengthW
, String_length
, 0}
1582 static const builtin_info_t StringInst_info
= {
1584 {NULL
, String_value
, 0},
1585 sizeof(StringInst_props
)/sizeof(*StringInst_props
),
1593 /* ECMA-262 3rd Edition 15.5.3.2 */
1594 static HRESULT
StringConstr_fromCharCode(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
1595 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1604 ret_str
= jsstr_alloc_buf(argc
, &ret
);
1606 return E_OUTOFMEMORY
;
1608 for(i
=0; i
<argc
; i
++) {
1609 hres
= to_uint32(ctx
, argv
[i
], &code
);
1619 *r
= jsval_string(ret
);
1625 static HRESULT
StringConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1637 hres
= to_string(ctx
, argv
[0], &str
);
1641 str
= jsstr_empty();
1644 *r
= jsval_string(str
);
1647 case DISPATCH_CONSTRUCT
: {
1652 hres
= to_string(ctx
, argv
[0], &str
);
1656 str
= jsstr_empty();
1659 hres
= create_string(ctx
, str
, &ret
);
1660 if (SUCCEEDED(hres
)) *r
= jsval_obj(ret
);
1666 FIXME("unimplemented flags: %x\n", flags
);
1673 static HRESULT
string_alloc(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsstr_t
*str
, StringInstance
**ret
)
1675 StringInstance
*string
;
1678 string
= heap_alloc_zero(sizeof(StringInstance
));
1680 return E_OUTOFMEMORY
;
1682 if(object_prototype
)
1683 hres
= init_dispex(&string
->dispex
, ctx
, &String_info
, object_prototype
);
1685 hres
= init_dispex_from_constr(&string
->dispex
, ctx
, &StringInst_info
, ctx
->string_constr
);
1691 string
->str
= jsstr_addref(str
);
1696 static const builtin_prop_t StringConstr_props
[] = {
1697 {fromCharCodeW
, StringConstr_fromCharCode
, PROPF_METHOD
},
1700 static const builtin_info_t StringConstr_info
= {
1702 {NULL
, Function_value
, 0},
1703 sizeof(StringConstr_props
)/sizeof(*StringConstr_props
),
1709 HRESULT
create_string_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
1711 StringInstance
*string
;
1714 static const WCHAR StringW
[] = {'S','t','r','i','n','g',0};
1716 hres
= string_alloc(ctx
, object_prototype
, jsstr_empty(), &string
);
1720 hres
= create_builtin_constructor(ctx
, StringConstr_value
, StringW
, &StringConstr_info
,
1721 PROPF_CONSTR
|1, &string
->dispex
, ret
);
1723 jsdisp_release(&string
->dispex
);
1727 HRESULT
create_string(script_ctx_t
*ctx
, jsstr_t
*str
, jsdisp_t
**ret
)
1729 StringInstance
*string
;
1732 hres
= string_alloc(ctx
, NULL
, str
, &string
);
1736 *ret
= &string
->dispex
;