ddraw/tests: Rewrite LimitTest().
[wine.git] / dlls / jscript / string.c
blobad978f245cb35126cf34cf60647a61748413a5c3
1 /*
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
19 #include "config.h"
20 #include "wine/port.h"
22 #include "jscript.h"
23 #include "regexp.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 jsdisp_t dispex;
31 jsstr_t *str;
32 } StringInstance;
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 localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
67 static const WCHAR fromCharCodeW[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
69 static inline StringInstance *string_from_jsdisp(jsdisp_t *jsdisp)
71 return CONTAINING_RECORD(jsdisp, StringInstance, dispex);
74 static inline StringInstance *string_from_vdisp(vdisp_t *vdisp)
76 return string_from_jsdisp(vdisp->u.jsdisp);
79 static inline StringInstance *string_this(vdisp_t *jsthis)
81 return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL;
84 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **val)
86 StringInstance *string;
88 if((string = string_this(jsthis))) {
89 *val = jsstr_addref(string->str);
90 return S_OK;
93 return to_string(ctx, jsval_disp(jsthis->u.disp), val);
96 static HRESULT get_string_flat_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **jsval, const WCHAR **val)
98 HRESULT hres;
100 hres = get_string_val(ctx, jsthis, jsval);
101 if(FAILED(hres))
102 return hres;
104 *val = jsstr_flatten(*jsval);
105 if(*val)
106 return S_OK;
108 jsstr_release(*jsval);
109 return E_OUTOFMEMORY;
112 static HRESULT String_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
114 StringInstance *string = string_from_jsdisp(jsthis);
116 TRACE("%p\n", jsthis);
118 *r = jsval_number(jsstr_length(string->str));
119 return S_OK;
122 static HRESULT String_set_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
124 FIXME("%p\n", jsthis);
125 return E_NOTIMPL;
128 static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r)
130 StringInstance *string;
132 if(!(string = string_this(jsthis))) {
133 WARN("this is not a string object\n");
134 return E_FAIL;
137 if(r)
138 *r = jsval_string(jsstr_addref(string->str));
139 return S_OK;
142 /* ECMA-262 3rd Edition 15.5.4.2 */
143 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
144 jsval_t *r)
146 TRACE("\n");
148 return stringobj_to_string(jsthis, r);
151 /* ECMA-262 3rd Edition 15.5.4.2 */
152 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
153 jsval_t *r)
155 TRACE("\n");
157 return stringobj_to_string(jsthis, r);
160 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t *r, const WCHAR *tagname)
162 unsigned tagname_len;
163 jsstr_t *str, *ret;
164 WCHAR *ptr;
165 HRESULT hres;
167 hres = get_string_val(ctx, jsthis, &str);
168 if(FAILED(hres))
169 return hres;
171 if(!r) {
172 jsstr_release(str);
173 return S_OK;
176 tagname_len = strlenW(tagname);
178 ret = jsstr_alloc_buf(jsstr_length(str) + 2*tagname_len + 5, &ptr);
179 if(!ret) {
180 jsstr_release(str);
181 return E_OUTOFMEMORY;
184 *ptr++ = '<';
185 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
186 ptr += tagname_len;
187 *ptr++ = '>';
189 ptr += jsstr_flush(str, ptr);
190 jsstr_release(str);
192 *ptr++ = '<';
193 *ptr++ = '/';
194 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
195 ptr += tagname_len;
196 *ptr = '>';
198 *r = jsval_string(ret);
199 return S_OK;
202 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, unsigned argc, jsval_t *argv, jsval_t *r,
203 const WCHAR *tagname, const WCHAR *attrname)
205 jsstr_t *str, *attr_value = NULL;
206 HRESULT hres;
208 hres = get_string_val(ctx, jsthis, &str);
209 if(FAILED(hres))
210 return hres;
212 if(argc) {
213 hres = to_string(ctx, argv[0], &attr_value);
214 if(FAILED(hres)) {
215 jsstr_release(str);
216 return hres;
218 }else {
219 attr_value = jsstr_undefined();
222 if(r) {
223 unsigned attrname_len = strlenW(attrname);
224 unsigned tagname_len = strlenW(tagname);
225 jsstr_t *ret;
226 WCHAR *ptr;
228 ret = jsstr_alloc_buf(2*tagname_len + attrname_len + jsstr_length(attr_value) + jsstr_length(str) + 9, &ptr);
229 if(ret) {
230 *ptr++ = '<';
231 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
232 ptr += tagname_len;
233 *ptr++ = ' ';
234 memcpy(ptr, attrname, attrname_len*sizeof(WCHAR));
235 ptr += attrname_len;
236 *ptr++ = '=';
237 *ptr++ = '"';
238 ptr += jsstr_flush(attr_value, ptr);
239 *ptr++ = '"';
240 *ptr++ = '>';
241 ptr += jsstr_flush(str, ptr);
243 *ptr++ = '<';
244 *ptr++ = '/';
245 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR));
246 ptr += tagname_len;
247 *ptr = '>';
249 *r = jsval_string(ret);
250 }else {
251 hres = E_OUTOFMEMORY;
255 jsstr_release(attr_value);
256 jsstr_release(str);
257 return hres;
260 static HRESULT String_anchor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
261 jsval_t *r)
263 static const WCHAR fontW[] = {'A',0};
264 static const WCHAR colorW[] = {'N','A','M','E',0};
266 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
269 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
270 jsval_t *r)
272 static const WCHAR bigtagW[] = {'B','I','G',0};
273 return do_attributeless_tag_format(ctx, jsthis, r, bigtagW);
276 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
277 jsval_t *r)
279 static const WCHAR blinktagW[] = {'B','L','I','N','K',0};
280 return do_attributeless_tag_format(ctx, jsthis, r, blinktagW);
283 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
284 jsval_t *r)
286 static const WCHAR boldtagW[] = {'B',0};
287 return do_attributeless_tag_format(ctx, jsthis, r, boldtagW);
290 /* ECMA-262 3rd Edition 15.5.4.5 */
291 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
292 jsval_t *r)
294 jsstr_t *str, *ret;
295 INT pos = 0;
296 HRESULT hres;
298 TRACE("\n");
300 hres = get_string_val(ctx, jsthis, &str);
301 if(FAILED(hres))
302 return hres;
304 if(argc) {
305 double d;
307 hres = to_integer(ctx, argv[0], &d);
308 if(FAILED(hres)) {
309 jsstr_release(str);
310 return hres;
312 pos = is_int32(d) ? d : -1;
315 if(!r) {
316 jsstr_release(str);
317 return S_OK;
320 if(0 <= pos && pos < jsstr_length(str)) {
321 ret = jsstr_substr(str, pos, 1);
322 if(!ret)
323 return E_OUTOFMEMORY;
324 }else {
325 ret = jsstr_empty();
328 *r = jsval_string(ret);
329 return S_OK;
332 /* ECMA-262 3rd Edition 15.5.4.5 */
333 static HRESULT String_charCodeAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
334 jsval_t *r)
336 jsstr_t *str;
337 DWORD idx = 0;
338 HRESULT hres;
340 TRACE("\n");
342 hres = get_string_val(ctx, jsthis, &str);
343 if(FAILED(hres))
344 return hres;
346 if(argc > 0) {
347 double d;
349 hres = to_integer(ctx, argv[0], &d);
350 if(FAILED(hres)) {
351 jsstr_release(str);
352 return hres;
355 if(!is_int32(d) || d < 0 || d >= jsstr_length(str)) {
356 jsstr_release(str);
357 if(r)
358 *r = jsval_number(NAN);
359 return S_OK;
362 idx = d;
365 if(r) {
366 WCHAR c;
367 jsstr_extract(str, idx, 1, &c);
368 *r = jsval_number(c);
371 jsstr_release(str);
372 return S_OK;
375 /* ECMA-262 3rd Edition 15.5.4.6 */
376 static HRESULT String_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
377 jsval_t *r)
379 jsstr_t *ret = NULL, *str;
380 HRESULT hres;
382 TRACE("\n");
384 hres = get_string_val(ctx, jsthis, &str);
385 if(FAILED(hres))
386 return hres;
388 switch(argc) {
389 case 0:
390 ret = str;
391 break;
392 case 1: {
393 jsstr_t *arg_str;
395 hres = to_string(ctx, argv[0], &arg_str);
396 if(FAILED(hres)) {
397 jsstr_release(str);
398 return hres;
401 ret = jsstr_concat(str, arg_str);
402 jsstr_release(str);
403 if(!ret)
404 return E_OUTOFMEMORY;
405 break;
407 default: {
408 const unsigned str_cnt = argc+1;
409 unsigned len = 0, i;
410 jsstr_t **strs;
411 WCHAR *ptr;
413 strs = heap_alloc_zero(str_cnt * sizeof(*strs));
414 if(!strs) {
415 jsstr_release(str);
416 return E_OUTOFMEMORY;
419 strs[0] = str;
420 for(i=0; i < argc; i++) {
421 hres = to_string(ctx, argv[i], strs+i+1);
422 if(FAILED(hres))
423 break;
426 if(SUCCEEDED(hres)) {
427 for(i=0; i < str_cnt; i++) {
428 len += jsstr_length(strs[i]);
429 if(len > JSSTR_MAX_LENGTH) {
430 hres = E_OUTOFMEMORY;
431 break;
435 if(SUCCEEDED(hres)) {
436 ret = jsstr_alloc_buf(len, &ptr);
437 if(ret) {
438 for(i=0; i < str_cnt; i++)
439 ptr += jsstr_flush(strs[i], ptr);
440 }else {
441 hres = E_OUTOFMEMORY;
446 while(i--)
447 jsstr_release(strs[i]);
448 heap_free(strs);
449 if(FAILED(hres))
450 return hres;
454 if(r)
455 *r = jsval_string(ret);
456 else
457 jsstr_release(ret);
458 return S_OK;
461 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
462 jsval_t *r)
464 static const WCHAR fixedtagW[] = {'T','T',0};
465 return do_attributeless_tag_format(ctx, jsthis, r, fixedtagW);
468 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
469 jsval_t *r)
471 static const WCHAR fontW[] = {'F','O','N','T',0};
472 static const WCHAR colorW[] = {'C','O','L','O','R',0};
474 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
477 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
478 jsval_t *r)
480 static const WCHAR fontW[] = {'F','O','N','T',0};
481 static const WCHAR colorW[] = {'S','I','Z','E',0};
483 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
486 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
487 jsval_t *r)
489 unsigned pos = 0, search_len, length;
490 jsstr_t *search_jsstr, *jsstr;
491 const WCHAR *search_str, *str;
492 INT ret = -1;
493 HRESULT hres;
495 TRACE("\n");
497 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
498 if(FAILED(hres))
499 return hres;
501 if(!argc) {
502 if(r)
503 *r = jsval_number(-1);
504 jsstr_release(jsstr);
505 return S_OK;
508 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
509 if(FAILED(hres)) {
510 jsstr_release(jsstr);
511 return hres;
514 search_len = jsstr_length(search_jsstr);
515 length = jsstr_length(jsstr);
517 if(argc >= 2) {
518 double d;
520 hres = to_integer(ctx, argv[1], &d);
521 if(SUCCEEDED(hres) && d > 0.0)
522 pos = is_int32(d) ? min(length, d) : length;
525 if(SUCCEEDED(hres) && length >= search_len) {
526 const WCHAR *end = str+length-search_len;
527 const WCHAR *ptr;
529 for(ptr = str+pos; ptr <= end; ptr++) {
530 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
531 ret = ptr-str;
532 break;
537 jsstr_release(search_jsstr);
538 jsstr_release(jsstr);
539 if(FAILED(hres))
540 return hres;
542 if(r)
543 *r = jsval_number(ret);
544 return S_OK;
547 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
548 jsval_t *r)
550 static const WCHAR italicstagW[] = {'I',0};
551 return do_attributeless_tag_format(ctx, jsthis, r, italicstagW);
554 /* ECMA-262 3rd Edition 15.5.4.8 */
555 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
556 jsval_t *r)
558 unsigned pos = 0, search_len, length;
559 jsstr_t *search_jsstr, *jsstr;
560 const WCHAR *search_str, *str;
561 INT ret = -1;
562 HRESULT hres;
564 TRACE("\n");
566 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
567 if(FAILED(hres))
568 return hres;
570 if(!argc) {
571 if(r)
572 *r = jsval_number(-1);
573 jsstr_release(jsstr);
574 return S_OK;
577 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str);
578 if(FAILED(hres)) {
579 jsstr_release(jsstr);
580 return hres;
583 search_len = jsstr_length(search_jsstr);
584 length = jsstr_length(jsstr);
586 if(argc >= 2) {
587 double d;
589 hres = to_integer(ctx, argv[1], &d);
590 if(SUCCEEDED(hres) && d > 0)
591 pos = is_int32(d) ? min(length, d) : length;
592 }else {
593 pos = length;
596 if(SUCCEEDED(hres) && length >= search_len) {
597 const WCHAR *ptr;
599 for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) {
600 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
601 ret = ptr-str;
602 break;
607 jsstr_release(search_jsstr);
608 jsstr_release(jsstr);
609 if(FAILED(hres))
610 return hres;
612 if(r)
613 *r = jsval_number(ret);
614 return S_OK;
617 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
618 jsval_t *r)
620 static const WCHAR fontW[] = {'A',0};
621 static const WCHAR colorW[] = {'H','R','E','F',0};
623 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW);
626 /* ECMA-262 3rd Edition 15.5.4.10 */
627 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
628 jsval_t *r)
630 jsdisp_t *regexp = NULL;
631 jsstr_t *str;
632 HRESULT hres;
634 TRACE("\n");
636 if(!argc) {
637 if(r)
638 *r = jsval_null();
639 return S_OK;
642 if(is_object_instance(argv[0])) {
643 regexp = iface_to_jsdisp(get_object(argv[0]));
644 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
645 jsdisp_release(regexp);
646 regexp = NULL;
650 if(!regexp) {
651 jsstr_t *match_str;
653 hres = to_string(ctx, argv[0], &match_str);
654 if(FAILED(hres))
655 return hres;
657 hres = create_regexp(ctx, match_str, 0, &regexp);
658 jsstr_release(match_str);
659 if(FAILED(hres))
660 return hres;
663 hres = get_string_val(ctx, jsthis, &str);
664 if(SUCCEEDED(hres))
665 hres = regexp_string_match(ctx, regexp, str, r);
667 jsdisp_release(regexp);
668 jsstr_release(str);
669 return hres;
672 typedef struct {
673 WCHAR *buf;
674 DWORD size;
675 DWORD len;
676 } strbuf_t;
678 static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len)
680 WCHAR *new_buf;
681 DWORD new_size;
683 if(len <= buf->size)
684 return TRUE;
686 new_size = buf->size ? buf->size<<1 : 16;
687 if(new_size < len)
688 new_size = len;
689 if(buf->buf)
690 new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
691 else
692 new_buf = heap_alloc(new_size*sizeof(WCHAR));
693 if(!new_buf)
694 return FALSE;
696 buf->buf = new_buf;
697 buf->size = new_size;
698 return TRUE;
701 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
703 if(!len)
704 return S_OK;
706 if(!strbuf_ensure_size(buf, buf->len+len))
707 return E_OUTOFMEMORY;
709 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
710 buf->len += len;
711 return S_OK;
714 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str)
716 if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str)))
717 return E_OUTOFMEMORY;
719 jsstr_flush(str, buf->buf+buf->len);
720 buf->len += jsstr_length(str);
721 return S_OK;
724 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func,
725 jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret)
727 jsval_t *argv;
728 unsigned argc;
729 jsval_t val;
730 jsstr_t *tmp_str;
731 DWORD i;
732 HRESULT hres = S_OK;
734 argc = match->paren_count+3;
735 argv = heap_alloc_zero(sizeof(*argv)*argc);
736 if(!argv)
737 return E_OUTOFMEMORY;
739 tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len);
740 if(!tmp_str)
741 hres = E_OUTOFMEMORY;
742 argv[0] = jsval_string(tmp_str);
744 if(SUCCEEDED(hres)) {
745 for(i=0; i < match->paren_count; i++) {
746 if(match->parens[i].index != -1)
747 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length);
748 else
749 tmp_str = jsstr_empty();
750 if(!tmp_str) {
751 hres = E_OUTOFMEMORY;
752 break;
754 argv[i+1] = jsval_string(tmp_str);
758 if(SUCCEEDED(hres)) {
759 argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len);
760 argv[match->paren_count+2] = jsval_string(jsstr);
763 if(SUCCEEDED(hres))
764 hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val);
766 for(i=0; i <= match->paren_count; i++)
767 jsstr_release(get_string(argv[i]));
768 heap_free(argv);
770 if(FAILED(hres))
771 return hres;
773 hres = to_string(ctx, val, ret);
774 jsval_release(val);
775 return hres;
778 /* ECMA-262 3rd Edition 15.5.4.11 */
779 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
780 jsval_t *r)
782 const WCHAR *str, *match_str = NULL, *rep_str = NULL;
783 jsstr_t *rep_jsstr, *match_jsstr, *jsstr;
784 jsdisp_t *rep_func = NULL, *regexp = NULL;
785 match_state_t *match = NULL, last_match = {0};
786 strbuf_t ret = {NULL,0,0};
787 DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT;
788 DWORD rep_len=0;
789 HRESULT hres = S_OK;
791 TRACE("\n");
793 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
794 if(FAILED(hres))
795 return hres;
797 if(!argc) {
798 if(r)
799 *r = jsval_string(jsstr);
800 else
801 jsstr_release(jsstr);
802 return S_OK;
805 if(is_object_instance(argv[0])) {
806 regexp = iface_to_jsdisp(get_object(argv[0]));
807 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
808 jsdisp_release(regexp);
809 regexp = NULL;
813 if(!regexp) {
814 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
815 if(FAILED(hres)) {
816 jsstr_release(jsstr);
817 return hres;
821 if(argc >= 2) {
822 if(is_object_instance(argv[1])) {
823 rep_func = iface_to_jsdisp(get_object(argv[1]));
824 if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) {
825 jsdisp_release(rep_func);
826 rep_func = NULL;
830 if(!rep_func) {
831 hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str);
832 if(SUCCEEDED(hres))
833 rep_len = jsstr_length(rep_jsstr);
837 if(SUCCEEDED(hres)) {
838 const WCHAR *ecp = str;
840 while(1) {
841 if(regexp) {
842 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match);
843 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT);
845 if(hres == S_FALSE) {
846 hres = S_OK;
847 break;
849 if(FAILED(hres))
850 break;
852 last_match.cp = match->cp;
853 last_match.match_len = match->match_len;
854 }else {
855 if(re_flags & REM_ALLOC_RESULT) {
856 re_flags &= ~REM_ALLOC_RESULT;
857 match = &last_match;
858 match->cp = str;
861 match->cp = strstrW(match->cp, match_str);
862 if(!match->cp)
863 break;
864 match->match_len = jsstr_length(match_jsstr);
865 match->cp += match->match_len;
868 hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len);
869 ecp = match->cp;
870 if(FAILED(hres))
871 break;
873 if(rep_func) {
874 jsstr_t *cstr;
876 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr);
877 if(FAILED(hres))
878 break;
880 hres = strbuf_append_jsstr(&ret, cstr);
881 jsstr_release(cstr);
882 if(FAILED(hres))
883 break;
884 }else if(rep_str && regexp) {
885 const WCHAR *ptr = rep_str, *ptr2;
887 while((ptr2 = strchrW(ptr, '$'))) {
888 hres = strbuf_append(&ret, ptr, ptr2-ptr);
889 if(FAILED(hres))
890 break;
892 switch(ptr2[1]) {
893 case '$':
894 hres = strbuf_append(&ret, ptr2, 1);
895 ptr = ptr2+2;
896 break;
897 case '&':
898 hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len);
899 ptr = ptr2+2;
900 break;
901 case '`':
902 hres = strbuf_append(&ret, str, match->cp-str-match->match_len);
903 ptr = ptr2+2;
904 break;
905 case '\'':
906 hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp);
907 ptr = ptr2+2;
908 break;
909 default: {
910 DWORD idx;
912 if(!isdigitW(ptr2[1])) {
913 hres = strbuf_append(&ret, ptr2, 1);
914 ptr = ptr2+1;
915 break;
918 idx = ptr2[1] - '0';
919 if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) {
920 idx = idx*10 + (ptr[2]-'0');
921 ptr = ptr2+3;
922 }else if(idx && idx <= match->paren_count) {
923 ptr = ptr2+2;
924 }else {
925 hres = strbuf_append(&ret, ptr2, 1);
926 ptr = ptr2+1;
927 break;
930 if(match->parens[idx-1].index != -1)
931 hres = strbuf_append(&ret, str+match->parens[idx-1].index,
932 match->parens[idx-1].length);
936 if(FAILED(hres))
937 break;
940 if(SUCCEEDED(hres))
941 hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
942 if(FAILED(hres))
943 break;
944 }else if(rep_str) {
945 hres = strbuf_append(&ret, rep_str, rep_len);
946 if(FAILED(hres))
947 break;
948 }else {
949 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
951 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
952 if(FAILED(hres))
953 break;
956 if(!regexp)
957 break;
958 else if(!match->match_len)
959 match->cp++;
962 if(SUCCEEDED(hres))
963 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp);
966 if(rep_func)
967 jsdisp_release(rep_func);
968 if(rep_str)
969 jsstr_release(rep_jsstr);
970 if(match_str)
971 jsstr_release(match_jsstr);
972 if(regexp)
973 heap_free(match);
975 if(SUCCEEDED(hres) && last_match.cp && regexp) {
976 jsstr_release(ctx->last_match);
977 ctx->last_match = jsstr_addref(jsstr);
978 ctx->last_match_index = last_match.cp-str-last_match.match_len;
979 ctx->last_match_length = last_match.match_len;
982 if(regexp)
983 jsdisp_release(regexp);
984 jsstr_release(jsstr);
986 if(SUCCEEDED(hres) && r) {
987 jsstr_t *ret_str;
989 ret_str = jsstr_alloc_len(ret.buf, ret.len);
990 if(!ret_str)
991 return E_OUTOFMEMORY;
993 TRACE("= %s\n", debugstr_jsstr(ret_str));
994 *r = jsval_string(ret_str);
997 heap_free(ret.buf);
998 return hres;
1001 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1002 jsval_t *r)
1004 jsdisp_t *regexp = NULL;
1005 const WCHAR *str;
1006 jsstr_t *jsstr;
1007 match_state_t match, *match_ptr = &match;
1008 HRESULT hres;
1010 TRACE("\n");
1012 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
1013 if(FAILED(hres))
1014 return hres;
1016 if(!argc) {
1017 if(r)
1018 *r = jsval_null();
1019 jsstr_release(jsstr);
1020 return S_OK;
1023 if(is_object_instance(argv[0])) {
1024 regexp = iface_to_jsdisp(get_object(argv[0]));
1025 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) {
1026 jsdisp_release(regexp);
1027 regexp = NULL;
1031 if(!regexp) {
1032 hres = create_regexp_var(ctx, argv[0], NULL, &regexp);
1033 if(FAILED(hres)) {
1034 jsstr_release(jsstr);
1035 return hres;
1039 match.cp = str;
1040 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr);
1041 jsstr_release(jsstr);
1042 jsdisp_release(regexp);
1043 if(FAILED(hres))
1044 return hres;
1046 if(r)
1047 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1);
1048 return S_OK;
1051 /* ECMA-262 3rd Edition 15.5.4.13 */
1052 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1053 jsval_t *r)
1055 int start=0, end, length;
1056 jsstr_t *str;
1057 double d;
1058 HRESULT hres;
1060 TRACE("\n");
1062 hres = get_string_val(ctx, jsthis, &str);
1063 if(FAILED(hres))
1064 return hres;
1066 length = jsstr_length(str);
1067 if(argc) {
1068 hres = to_integer(ctx, argv[0], &d);
1069 if(FAILED(hres)) {
1070 jsstr_release(str);
1071 return hres;
1074 if(is_int32(d)) {
1075 start = d;
1076 if(start < 0) {
1077 start = length + start;
1078 if(start < 0)
1079 start = 0;
1080 }else if(start > length) {
1081 start = length;
1083 }else if(d > 0) {
1084 start = length;
1088 if(argc >= 2) {
1089 hres = to_integer(ctx, argv[1], &d);
1090 if(FAILED(hres)) {
1091 jsstr_release(str);
1092 return hres;
1095 if(is_int32(d)) {
1096 end = d;
1097 if(end < 0) {
1098 end = length + end;
1099 if(end < 0)
1100 end = 0;
1101 }else if(end > length) {
1102 end = length;
1104 }else {
1105 end = d < 0.0 ? 0 : length;
1107 }else {
1108 end = length;
1111 if(end < start)
1112 end = start;
1114 if(r) {
1115 jsstr_t *retstr = jsstr_substr(str, start, end-start);
1116 if(!retstr) {
1117 jsstr_release(str);
1118 return E_OUTOFMEMORY;
1121 *r = jsval_string(retstr);
1124 jsstr_release(str);
1125 return S_OK;
1128 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1129 jsval_t *r)
1131 static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1132 return do_attributeless_tag_format(ctx, jsthis, r, smalltagW);
1135 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1136 jsval_t *r)
1138 match_state_t match_result, *match_ptr = &match_result;
1139 DWORD length, i, match_len = 0;
1140 const WCHAR *ptr, *ptr2, *str, *match_str = NULL;
1141 unsigned limit = ~0u;
1142 jsdisp_t *array, *regexp = NULL;
1143 jsstr_t *jsstr, *match_jsstr, *tmp_str;
1144 HRESULT hres;
1146 TRACE("\n");
1148 if(argc != 1 && argc != 2) {
1149 FIXME("unsupported argc %u\n", argc);
1150 return E_NOTIMPL;
1153 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str);
1154 if(FAILED(hres))
1155 return hres;
1157 length = jsstr_length(jsstr);
1159 if(argc > 1 && !is_undefined(argv[1])) {
1160 hres = to_uint32(ctx, argv[1], &limit);
1161 if(FAILED(hres)) {
1162 jsstr_release(jsstr);
1163 return hres;
1167 if(is_object_instance(argv[0])) {
1168 regexp = iface_to_jsdisp(get_object(argv[0]));
1169 if(regexp) {
1170 if(!is_class(regexp, JSCLASS_REGEXP)) {
1171 jsdisp_release(regexp);
1172 regexp = NULL;
1177 if(!regexp) {
1178 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str);
1179 if(FAILED(hres)) {
1180 jsstr_release(jsstr);
1181 return hres;
1184 match_len = jsstr_length(match_jsstr);
1185 if(!match_len) {
1186 jsstr_release(match_jsstr);
1187 match_str = NULL;
1191 hres = create_array(ctx, 0, &array);
1193 if(SUCCEEDED(hres)) {
1194 ptr = str;
1195 match_result.cp = str;
1196 for(i=0; i<limit; i++) {
1197 if(regexp) {
1198 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr);
1199 if(hres != S_OK)
1200 break;
1201 ptr2 = match_result.cp - match_result.match_len;
1202 }else if(match_str) {
1203 ptr2 = strstrW(ptr, match_str);
1204 if(!ptr2)
1205 break;
1206 }else {
1207 if(!*ptr)
1208 break;
1209 ptr2 = ptr+1;
1212 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr);
1213 if(!tmp_str) {
1214 hres = E_OUTOFMEMORY;
1215 break;
1218 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1219 jsstr_release(tmp_str);
1220 if(FAILED(hres))
1221 break;
1223 if(regexp)
1224 ptr = match_result.cp;
1225 else if(match_str)
1226 ptr = ptr2 + match_len;
1227 else
1228 ptr++;
1232 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) {
1233 DWORD len = (str+length) - ptr;
1235 if(len || match_str) {
1236 tmp_str = jsstr_alloc_len(ptr, len);
1238 if(tmp_str) {
1239 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
1240 jsstr_release(tmp_str);
1241 }else {
1242 hres = E_OUTOFMEMORY;
1247 if(regexp)
1248 jsdisp_release(regexp);
1249 if(match_str)
1250 jsstr_release(match_jsstr);
1251 jsstr_release(jsstr);
1253 if(SUCCEEDED(hres) && r)
1254 *r = jsval_obj(array);
1255 else
1256 jsdisp_release(array);
1258 return hres;
1261 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1262 jsval_t *r)
1264 static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1265 return do_attributeless_tag_format(ctx, jsthis, r, striketagW);
1268 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1269 jsval_t *r)
1271 static const WCHAR subtagW[] = {'S','U','B',0};
1272 return do_attributeless_tag_format(ctx, jsthis, r, subtagW);
1275 /* ECMA-262 3rd Edition 15.5.4.15 */
1276 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1277 jsval_t *r)
1279 INT start=0, end, length;
1280 jsstr_t *str;
1281 double d;
1282 HRESULT hres;
1284 TRACE("\n");
1286 hres = get_string_val(ctx, jsthis, &str);
1287 if(FAILED(hres))
1288 return hres;
1290 length = jsstr_length(str);
1291 if(argc >= 1) {
1292 hres = to_integer(ctx, argv[0], &d);
1293 if(FAILED(hres)) {
1294 jsstr_release(str);
1295 return hres;
1298 if(d >= 0)
1299 start = is_int32(d) ? min(length, d) : length;
1302 if(argc >= 2) {
1303 hres = to_integer(ctx, argv[1], &d);
1304 if(FAILED(hres)) {
1305 jsstr_release(str);
1306 return hres;
1309 if(d >= 0)
1310 end = is_int32(d) ? min(length, d) : length;
1311 else
1312 end = 0;
1313 }else {
1314 end = length;
1317 if(start > end) {
1318 INT tmp = start;
1319 start = end;
1320 end = tmp;
1323 if(r) {
1324 jsstr_t *ret = jsstr_substr(str, start, end-start);
1325 if(ret)
1326 *r = jsval_string(ret);
1327 else
1328 hres = E_OUTOFMEMORY;
1330 jsstr_release(str);
1331 return hres;
1334 /* ECMA-262 3rd Edition B.2.3 */
1335 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1336 jsval_t *r)
1338 int start=0, len, length;
1339 jsstr_t *str;
1340 double d;
1341 HRESULT hres;
1343 TRACE("\n");
1345 hres = get_string_val(ctx, jsthis, &str);
1346 if(FAILED(hres))
1347 return hres;
1349 length = jsstr_length(str);
1350 if(argc >= 1) {
1351 hres = to_integer(ctx, argv[0], &d);
1352 if(FAILED(hres)) {
1353 jsstr_release(str);
1354 return hres;
1357 if(d >= 0)
1358 start = is_int32(d) ? min(length, d) : length;
1361 if(argc >= 2) {
1362 hres = to_integer(ctx, argv[1], &d);
1363 if(FAILED(hres)) {
1364 jsstr_release(str);
1365 return hres;
1368 if(d >= 0.0)
1369 len = is_int32(d) ? min(length-start, d) : length-start;
1370 else
1371 len = 0;
1372 }else {
1373 len = length-start;
1376 hres = S_OK;
1377 if(r) {
1378 jsstr_t *ret = jsstr_substr(str, start, len);
1379 if(ret)
1380 *r = jsval_string(ret);
1381 else
1382 hres = E_OUTOFMEMORY;
1385 jsstr_release(str);
1386 return hres;
1389 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1390 jsval_t *r)
1392 static const WCHAR suptagW[] = {'S','U','P',0};
1393 return do_attributeless_tag_format(ctx, jsthis, r, suptagW);
1396 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1397 jsval_t *r)
1399 jsstr_t *str;
1400 HRESULT hres;
1402 TRACE("\n");
1404 hres = get_string_val(ctx, jsthis, &str);
1405 if(FAILED(hres))
1406 return hres;
1408 if(r) {
1409 unsigned len = jsstr_length(str);
1410 jsstr_t *ret;
1411 WCHAR *buf;
1413 ret = jsstr_alloc_buf(len, &buf);
1414 if(!ret) {
1415 jsstr_release(str);
1416 return E_OUTOFMEMORY;
1419 jsstr_flush(str, buf);
1420 for (; len--; buf++) *buf = tolowerW(*buf);
1422 *r = jsval_string(ret);
1424 jsstr_release(str);
1425 return S_OK;
1428 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1429 jsval_t *r)
1431 jsstr_t *str;
1432 HRESULT hres;
1434 TRACE("\n");
1436 hres = get_string_val(ctx, jsthis, &str);
1437 if(FAILED(hres))
1438 return hres;
1440 if(r) {
1441 unsigned len = jsstr_length(str);
1442 jsstr_t *ret;
1443 WCHAR *buf;
1445 ret = jsstr_alloc_buf(len, &buf);
1446 if(!ret) {
1447 jsstr_release(str);
1448 return E_OUTOFMEMORY;
1451 jsstr_flush(str, buf);
1452 for (; len--; buf++) *buf = toupperW(*buf);
1454 *r = jsval_string(ret);
1456 jsstr_release(str);
1457 return S_OK;
1460 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1461 jsval_t *r)
1463 FIXME("\n");
1464 return E_NOTIMPL;
1467 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1468 jsval_t *r)
1470 FIXME("\n");
1471 return E_NOTIMPL;
1474 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1475 jsval_t *r)
1477 FIXME("\n");
1478 return E_NOTIMPL;
1481 static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
1483 StringInstance *This = string_from_jsdisp(jsthis);
1485 TRACE("\n");
1487 *r = jsval_string(jsstr_addref(This->str));
1488 return S_OK;
1491 static void String_destructor(jsdisp_t *dispex)
1493 StringInstance *This = string_from_jsdisp(dispex);
1495 jsstr_release(This->str);
1496 heap_free(This);
1499 static unsigned String_idx_length(jsdisp_t *jsdisp)
1501 StringInstance *string = string_from_jsdisp(jsdisp);
1504 * NOTE: For invoke version < 2, indexed array is not implemented at all.
1505 * Newer jscript.dll versions implement it on string type, not class,
1506 * which is not how it should work according to spec. IE9 implements it
1507 * properly, but it uses its own JavaScript engine inside MSHTML. We
1508 * implement it here, but in the way IE9 and spec work.
1510 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str);
1513 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
1515 StringInstance *string = string_from_jsdisp(jsdisp);
1516 jsstr_t *ret;
1518 ret = jsstr_substr(string->str, idx, 1);
1519 if(!ret)
1520 return E_OUTOFMEMORY;
1522 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret));
1524 *r = jsval_string(ret);
1525 return S_OK;
1528 static const builtin_prop_t String_props[] = {
1529 {anchorW, String_anchor, PROPF_METHOD|1},
1530 {bigW, String_big, PROPF_METHOD},
1531 {blinkW, String_blink, PROPF_METHOD},
1532 {boldW, String_bold, PROPF_METHOD},
1533 {charAtW, String_charAt, PROPF_METHOD|1},
1534 {charCodeAtW, String_charCodeAt, PROPF_METHOD|1},
1535 {concatW, String_concat, PROPF_METHOD|1},
1536 {fixedW, String_fixed, PROPF_METHOD},
1537 {fontcolorW, String_fontcolor, PROPF_METHOD|1},
1538 {fontsizeW, String_fontsize, PROPF_METHOD|1},
1539 {indexOfW, String_indexOf, PROPF_METHOD|2},
1540 {italicsW, String_italics, PROPF_METHOD},
1541 {lastIndexOfW, String_lastIndexOf, PROPF_METHOD|2},
1542 {lengthW, NULL,0, String_get_length, String_set_length},
1543 {linkW, String_link, PROPF_METHOD|1},
1544 {localeCompareW, String_localeCompare, PROPF_METHOD|1},
1545 {matchW, String_match, PROPF_METHOD|1},
1546 {replaceW, String_replace, PROPF_METHOD|1},
1547 {searchW, String_search, PROPF_METHOD},
1548 {sliceW, String_slice, PROPF_METHOD},
1549 {smallW, String_small, PROPF_METHOD},
1550 {splitW, String_split, PROPF_METHOD|2},
1551 {strikeW, String_strike, PROPF_METHOD},
1552 {subW, String_sub, PROPF_METHOD},
1553 {substrW, String_substr, PROPF_METHOD|2},
1554 {substringW, String_substring, PROPF_METHOD|2},
1555 {supW, String_sup, PROPF_METHOD},
1556 {toLocaleLowerCaseW, String_toLocaleLowerCase, PROPF_METHOD},
1557 {toLocaleUpperCaseW, String_toLocaleUpperCase, PROPF_METHOD},
1558 {toLowerCaseW, String_toLowerCase, PROPF_METHOD},
1559 {toStringW, String_toString, PROPF_METHOD},
1560 {toUpperCaseW, String_toUpperCase, PROPF_METHOD},
1561 {valueOfW, String_valueOf, PROPF_METHOD}
1564 static const builtin_info_t String_info = {
1565 JSCLASS_STRING,
1566 {NULL, NULL,0, String_get_value},
1567 sizeof(String_props)/sizeof(*String_props),
1568 String_props,
1569 String_destructor,
1570 NULL
1573 static const builtin_prop_t StringInst_props[] = {
1574 {lengthW, NULL,0, String_get_length, String_set_length}
1577 static const builtin_info_t StringInst_info = {
1578 JSCLASS_STRING,
1579 {NULL, NULL,0, String_get_value},
1580 sizeof(StringInst_props)/sizeof(*StringInst_props),
1581 StringInst_props,
1582 String_destructor,
1583 NULL,
1584 String_idx_length,
1585 String_idx_get
1588 /* ECMA-262 3rd Edition 15.5.3.2 */
1589 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1590 unsigned argc, jsval_t *argv, jsval_t *r)
1592 WCHAR *ret_str;
1593 DWORD i, code;
1594 jsstr_t *ret;
1595 HRESULT hres;
1597 TRACE("\n");
1599 ret = jsstr_alloc_buf(argc, &ret_str);
1600 if(!ret)
1601 return E_OUTOFMEMORY;
1603 for(i=0; i<argc; i++) {
1604 hres = to_uint32(ctx, argv[i], &code);
1605 if(FAILED(hres)) {
1606 jsstr_release(ret);
1607 return hres;
1610 ret_str[i] = code;
1613 if(r)
1614 *r = jsval_string(ret);
1615 else
1616 jsstr_release(ret);
1617 return S_OK;
1620 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1621 jsval_t *r)
1623 HRESULT hres;
1625 TRACE("\n");
1627 switch(flags) {
1628 case INVOKE_FUNC: {
1629 jsstr_t *str;
1631 if(argc) {
1632 hres = to_string(ctx, argv[0], &str);
1633 if(FAILED(hres))
1634 return hres;
1635 }else {
1636 str = jsstr_empty();
1639 *r = jsval_string(str);
1640 break;
1642 case DISPATCH_CONSTRUCT: {
1643 jsstr_t *str;
1644 jsdisp_t *ret;
1646 if(argc) {
1647 hres = to_string(ctx, argv[0], &str);
1648 if(FAILED(hres))
1649 return hres;
1650 }else {
1651 str = jsstr_empty();
1654 hres = create_string(ctx, str, &ret);
1655 if (SUCCEEDED(hres)) *r = jsval_obj(ret);
1656 jsstr_release(str);
1657 return hres;
1660 default:
1661 FIXME("unimplemented flags: %x\n", flags);
1662 return E_NOTIMPL;
1665 return S_OK;
1668 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret)
1670 StringInstance *string;
1671 HRESULT hres;
1673 string = heap_alloc_zero(sizeof(StringInstance));
1674 if(!string)
1675 return E_OUTOFMEMORY;
1677 if(object_prototype)
1678 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1679 else
1680 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr);
1681 if(FAILED(hres)) {
1682 heap_free(string);
1683 return hres;
1686 string->str = jsstr_addref(str);
1687 *ret = string;
1688 return S_OK;
1691 static const builtin_prop_t StringConstr_props[] = {
1692 {fromCharCodeW, StringConstr_fromCharCode, PROPF_METHOD},
1695 static const builtin_info_t StringConstr_info = {
1696 JSCLASS_FUNCTION,
1697 DEFAULT_FUNCTION_VALUE,
1698 sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1699 StringConstr_props,
1700 NULL,
1701 NULL
1704 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1706 StringInstance *string;
1707 HRESULT hres;
1709 static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1711 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string);
1712 if(FAILED(hres))
1713 return hres;
1715 hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info,
1716 PROPF_CONSTR|1, &string->dispex, ret);
1718 jsdisp_release(&string->dispex);
1719 return hres;
1722 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret)
1724 StringInstance *string;
1725 HRESULT hres;
1727 hres = string_alloc(ctx, NULL, str, &string);
1728 if(FAILED(hres))
1729 return hres;
1731 *ret = &string->dispex;
1732 return S_OK;