jsstr_t *jsstr_concat(jsstr_t *str1, jsstr_t *str2) { unsigned len1, len2; jsstr_t *ret; WCHAR *ptr; len1 = jsstr_length(str1); if(!len1) return jsstr_addref(str2); len2 = jsstr_length(str2); if(!len2) return jsstr_addref(str1); if(len1 + len2 >= JSSTR_SHORT_STRING_LENGTH) { unsigned depth, depth2; jsstr_rope_t *rope; depth = jsstr_is_rope(str1) ? jsstr_as_rope(str1)->depth : 0; depth2 = jsstr_is_rope(str2) ? jsstr_as_rope(str2)->depth : 0; if(depth2 > depth) depth = depth2; if(depth++ < JSSTR_MAX_ROPE_DEPTH) { if(len1+len2 > JSSTR_MAX_LENGTH) return NULL; rope = heap_alloc(sizeof(*rope)); if(!rope) return NULL; jsstr_init(&rope->str, len1+len2, JSSTR_ROPE); rope->left = jsstr_addref(str1); rope->right = jsstr_addref(str2); rope->depth = depth; return &rope->str; } } ptr = jsstr_alloc_buf(len1+len2, &ret); if(!ret) return NULL; jsstr_flush(str1, ptr); jsstr_flush(str2, ptr+len1); return ret; }
HRESULT jsval_copy(jsval_t v, jsval_t *r) { switch(jsval_type(v)) { case JSV_UNDEFINED: case JSV_NULL: case JSV_NUMBER: case JSV_BOOL: *r = v; return S_OK; case JSV_OBJECT: if(get_object(v)) IDispatch_AddRef(get_object(v)); *r = v; return S_OK; case JSV_STRING: { jsstr_addref(get_string(v)); *r = v; return S_OK; } case JSV_VARIANT: return jsval_variant(r, get_variant(v)); } assert(0); return E_FAIL; }
HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret) { RegExpInstance *regexp; HRESULT hres; TRACE("%s %x\n", debugstr_jsstr(src), flags); hres = alloc_regexp(ctx, NULL, ®exp); if(FAILED(hres)) return hres; regexp->str = jsstr_addref(src); regexp->last_index_val = jsval_number(0); regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, regexp->str->str, jsstr_length(regexp->str), flags, FALSE); if(FAILED(hres)) { WARN("regexp_new failed\n"); jsdisp_release(®exp->dispex); return E_FAIL; } *ret = ®exp->dispex; return S_OK; }
static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { StringInstance *This = (StringInstance*)jsthis; TRACE("\n"); *r = jsval_string(jsstr_addref(This->str)); return S_OK; }
static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **val) { StringInstance *string; if((string = string_this(jsthis))) { *val = jsstr_addref(string->str); return S_OK; } return to_string(ctx, jsval_disp(jsthis->u.disp), val); }
jsstr_t *jsstr_concat(jsstr_t *str1, jsstr_t *str2) { unsigned len1, len2; jsstr_t *ret; len1 = jsstr_length(str1); if(!len1) return jsstr_addref(str2); len2 = jsstr_length(str2); if(!len2) return jsstr_addref(str1); ret = jsstr_alloc_buf(len1+len2); if(!ret) return NULL; memcpy(ret->str, str1->str, len1*sizeof(WCHAR)); memcpy(ret->str+len1, str2->str, len2*sizeof(WCHAR)); return ret; }
jsstr_t *jsstr_concat(jsstr_t *str1, jsstr_t *str2) { unsigned len1, len2; jsstr_t *ret; len1 = jsstr_length(str1); if(!len1) return jsstr_addref(str2); len2 = jsstr_length(str2); if(!len2) return jsstr_addref(str1); ret = jsstr_alloc_buf(len1+len2); if(!ret) return NULL; jsstr_flush(str1, ret->str); jsstr_flush(str2, ret->str+len1); return ret; }
static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r) { StringInstance *string; if(!(string = string_this(jsthis))) { WARN("this is not a string object\n"); return E_FAIL; } if(r) *r = jsval_string(jsstr_addref(string->str)); return S_OK; }
static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp, DWORD rem_flags, jsstr_t *str, match_state_t *ret) { HRESULT hres; hres = regexp_execute(regexp->jsregexp, ctx, &ctx->tmp_heap, str->str, jsstr_length(str), ret); if(FAILED(hres)) return hres; if(hres == S_FALSE) { if(rem_flags & REM_RESET_INDEX) set_last_index(regexp, 0); return S_FALSE; } if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != str) { jsstr_release(ctx->last_match); ctx->last_match = jsstr_addref(str); } if(!(rem_flags & REM_NO_CTX_UPDATE)) { DWORD i, n = min(sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]), ret->paren_count); for(i=0; i < n; i++) { if(ret->parens[i].index == -1) { ctx->match_parens[i].index = 0; ctx->match_parens[i].length = 0; }else { ctx->match_parens[i].index = ret->parens[i].index; ctx->match_parens[i].length = ret->parens[i].length; } } if(n < sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0])) memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0])); } set_last_index(regexp, ret->cp-str->str); if(!(rem_flags & REM_NO_CTX_UPDATE)) { ctx->last_match_index = ret->cp-str->str-ret->match_len; ctx->last_match_length = ret->match_len; } return S_OK; }
static HRESULT RegExp_source(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); switch(flags) { case DISPATCH_PROPERTYGET: { RegExpInstance *This = regexp_from_vdisp(jsthis); *r = jsval_string(jsstr_addref(This->str)); break; } default: FIXME("Unimplemented flags %x\n", flags); return E_NOTIMPL; } return S_OK; }
/* ECMA-262 3rd Edition 9.8 */ HRESULT to_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str) { const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0}; const WCHAR nullW[] = {'n','u','l','l',0}; const WCHAR trueW[] = {'t','r','u','e',0}; const WCHAR falseW[] = {'f','a','l','s','e',0}; switch(jsval_type(val)) { case JSV_UNDEFINED: *str = jsstr_alloc(undefinedW); break; case JSV_NULL: *str = jsstr_alloc(nullW); break; case JSV_NUMBER: return double_to_string(get_number(val), str); case JSV_STRING: *str = jsstr_addref(get_string(val)); break; case JSV_OBJECT: { jsval_t prim; HRESULT hres; hres = to_primitive(ctx, val, &prim, HINT_STRING); if(FAILED(hres)) return hres; hres = to_string(ctx, prim, str); jsval_release(prim); return hres; } case JSV_BOOL: *str = jsstr_alloc(get_bool(val) ? trueW : falseW); break; default: FIXME("unsupported %s\n", debugstr_jsval(val)); return E_NOTIMPL; } return *str ? S_OK : E_OUTOFMEMORY; }
static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret) { StringInstance *string; HRESULT hres; string = heap_alloc_zero(sizeof(StringInstance)); if(!string) return E_OUTOFMEMORY; if(object_prototype) hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype); else hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr); if(FAILED(hres)) { heap_free(string); return hres; } string->str = jsstr_addref(str); *ret = string; return S_OK; }
static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input, const match_state_t *result, IDispatch **ret) { jsdisp_t *array; jsstr_t *str; DWORD i; HRESULT hres = S_OK; static const WCHAR indexW[] = {'i','n','d','e','x',0}; static const WCHAR inputW[] = {'i','n','p','u','t',0}; static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; static const WCHAR zeroW[] = {'0',0}; hres = create_array(ctx, result->paren_count+1, &array); if(FAILED(hres)) return hres; for(i=0; i < result->paren_count; i++) { if(result->parens[i].index != -1) str = jsstr_substr(input, result->parens[i].index, result->parens[i].length); else str = jsstr_empty(); if(!str) { hres = E_OUTOFMEMORY; break; } hres = jsdisp_propput_idx(array, i+1, jsval_string(str)); jsstr_release(str); if(FAILED(hres)) break; } while(SUCCEEDED(hres)) { hres = jsdisp_propput_name(array, indexW, jsval_number(result->cp-input->str-result->match_len)); if(FAILED(hres)) break; hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->cp-input->str)); if(FAILED(hres)) break; hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input))); if(FAILED(hres)) break; str = jsstr_alloc_len(result->cp-result->match_len, result->match_len); if(!str) { hres = E_OUTOFMEMORY; break; } hres = jsdisp_propput_name(array, zeroW, jsval_string(str)); jsstr_release(str); break; } if(FAILED(hres)) { jsdisp_release(array); return hres; } *ret = to_disp(array); return S_OK; }
jsstr_t *jsstr_null_bstr(void) { return jsstr_addref(null_bstr_str); }
jsstr_t *jsstr_undefined(void) { return jsstr_addref(undefined_str); }
jsstr_t *jsstr_empty(void) { return jsstr_addref(empty_str); }
jsstr_t *jsstr_nan(void) { return jsstr_addref(nan_str); }
/* ECMA-262 3rd Edition 15.5.4.11 */ static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { const WCHAR *str, *match_str = NULL, *rep_str = NULL; jsstr_t *rep_jsstr, *match_jsstr, *jsstr; jsdisp_t *rep_func = NULL, *regexp = NULL; match_state_t *match = NULL, last_match = {0}; strbuf_t ret = {NULL,0,0}; DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT; DWORD rep_len=0; HRESULT hres = S_OK; TRACE("\n"); hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); if(FAILED(hres)) return hres; if(!argc) { if(r) *r = jsval_string(jsstr); else jsstr_release(jsstr); return S_OK; } if(is_object_instance(argv[0])) { regexp = iface_to_jsdisp(get_object(argv[0])); if(regexp && !is_class(regexp, JSCLASS_REGEXP)) { jsdisp_release(regexp); regexp = NULL; } } if(!regexp) { hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str); if(FAILED(hres)) { jsstr_release(jsstr); return hres; } } if(argc >= 2) { if(is_object_instance(argv[1])) { rep_func = iface_to_jsdisp(get_object(argv[1])); if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) { jsdisp_release(rep_func); rep_func = NULL; } } if(!rep_func) { hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str); if(SUCCEEDED(hres)) rep_len = jsstr_length(rep_jsstr); } } if(SUCCEEDED(hres)) { const WCHAR *ecp = str; while(1) { if(regexp) { hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match); re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT); if(hres == S_FALSE) { hres = S_OK; break; } if(FAILED(hres)) break; last_match.cp = match->cp; last_match.match_len = match->match_len; } else { if(re_flags & REM_ALLOC_RESULT) { re_flags &= ~REM_ALLOC_RESULT; match = &last_match; match->cp = str; } match->cp = strstrW(match->cp, match_str); if(!match->cp) break; match->match_len = jsstr_length(match_jsstr); match->cp += match->match_len; } hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len); ecp = match->cp; if(FAILED(hres)) break; if(rep_func) { jsstr_t *cstr; hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr); if(FAILED(hres)) break; hres = strbuf_append_jsstr(&ret, cstr); jsstr_release(cstr); if(FAILED(hres)) break; } else if(rep_str && regexp) { const WCHAR *ptr = rep_str, *ptr2; while((ptr2 = strchrW(ptr, '$'))) { hres = strbuf_append(&ret, ptr, ptr2-ptr); if(FAILED(hres)) break; switch(ptr2[1]) { case '$': hres = strbuf_append(&ret, ptr2, 1); ptr = ptr2+2; break; case '&': hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len); ptr = ptr2+2; break; case '`': hres = strbuf_append(&ret, str, match->cp-str-match->match_len); ptr = ptr2+2; break; case '\'': hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp); ptr = ptr2+2; break; default: { DWORD idx; if(!isdigitW(ptr2[1])) { hres = strbuf_append(&ret, ptr2, 1); ptr = ptr2+1; break; } idx = ptr2[1] - '0'; if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) { idx = idx*10 + (ptr[2]-'0'); ptr = ptr2+3; } else if(idx && idx <= match->paren_count) { ptr = ptr2+2; } else { hres = strbuf_append(&ret, ptr2, 1); ptr = ptr2+1; break; } if(match->parens[idx-1].index != -1) hres = strbuf_append(&ret, str+match->parens[idx-1].index, match->parens[idx-1].length); } } if(FAILED(hres)) break; } if(SUCCEEDED(hres)) hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr); if(FAILED(hres)) break; } else if(rep_str) { hres = strbuf_append(&ret, rep_str, rep_len); if(FAILED(hres)) break; } else { static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'}; hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR)); if(FAILED(hres)) break; } if(!regexp) break; else if(!match->match_len) match->cp++; } if(SUCCEEDED(hres)) hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp); } if(rep_func) jsdisp_release(rep_func); if(rep_str) jsstr_release(rep_jsstr); if(match_str) jsstr_release(match_jsstr); if(regexp) heap_free(match); if(SUCCEEDED(hres) && last_match.cp && regexp) { jsstr_release(ctx->last_match); ctx->last_match = jsstr_addref(jsstr); ctx->last_match_index = last_match.cp-str-last_match.match_len; ctx->last_match_length = last_match.match_len; } if(regexp) jsdisp_release(regexp); jsstr_release(jsstr); if(SUCCEEDED(hres) && r) { jsstr_t *ret_str; ret_str = jsstr_alloc_len(ret.buf, ret.len); if(!ret_str) return E_OUTOFMEMORY; TRACE("= %s\n", debugstr_jsstr(ret_str)); *r = jsval_string(ret_str); } heap_free(ret.buf); return hres; }