static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This) { HRESULT hres; VARIANT nextitem; if (This->atend) return S_OK; /* dont leak pervious value */ jsval_release(This->item); /* not at end ... get next item */ VariantInit(&nextitem); hres = IEnumVARIANT_Next(This->enumvar, 1, &nextitem, NULL); if (hres == S_OK) { hres = variant_to_jsval(&nextitem, &This->item); VariantClear(&nextitem); if (FAILED(hres)) { WARN("failed to convert jsval to variant!"); This->item = jsval_undefined(); return hres; } } else { This->item = jsval_undefined(); This->atend = TRUE; } return S_OK; }
/* ECMA-262 3rd Edition 15.4.4.9 */ static HRESULT Array_shift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsthis; DWORD length = 0, i; jsval_t v, ret; HRESULT hres; TRACE("\n"); hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; if(!length) { hres = set_length(jsthis, 0); if(FAILED(hres)) return hres; } if(!length) { if(r) *r = jsval_undefined(); return S_OK; } hres = jsdisp_get_idx(jsthis, 0, &ret); if(hres == DISP_E_UNKNOWNNAME) { ret = jsval_undefined(); hres = S_OK; } for(i=1; SUCCEEDED(hres) && i<length; i++) { hres = jsdisp_get_idx(jsthis, i, &v); if(hres == DISP_E_UNKNOWNNAME) hres = jsdisp_delete_idx(jsthis, i-1); else if(SUCCEEDED(hres)) hres = jsdisp_propput_idx(jsthis, i-1, v); } if(SUCCEEDED(hres)) { hres = jsdisp_delete_idx(jsthis, length-1); if(SUCCEEDED(hres)) hres = set_length(jsthis, length-1); } if(FAILED(hres)) return hres; if(r) *r = ret; else jsval_release(ret); return hres; }
static HRESULT Array_pop(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsthis; jsval_t val; DWORD length; HRESULT hres; TRACE("\n"); hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; if(!length) { hres = set_length(jsthis, 0); if(FAILED(hres)) return hres; if(r) *r = jsval_undefined(); return S_OK; } length--; hres = jsdisp_get_idx(jsthis, length, &val); if(SUCCEEDED(hres)) hres = jsdisp_delete_idx(jsthis, length); else if(hres == DISP_E_UNKNOWNNAME) { val = jsval_undefined(); hres = S_OK; }else return hres; if(SUCCEEDED(hres)) hres = set_length(jsthis, length); if(FAILED(hres)) { jsval_release(val); return hres; } if(r) *r = val; else jsval_release(val); return hres; }
/* ECMA-262 5.1 Edition 15.12.3 */ static HRESULT JSON_stringify(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { stringify_ctx_t stringify_ctx = {ctx, NULL,0,0, NULL,0,0, {0}}; HRESULT hres; TRACE("\n"); if(argc >= 2 && is_object_instance(argv[1])) { FIXME("Replacer %s not yet supported\n", debugstr_jsval(argv[1])); return E_NOTIMPL; } if(argc >= 3) { jsval_t space_val; hres = maybe_to_primitive(ctx, argv[2], &space_val); if(FAILED(hres)) return hres; if(is_number(space_val)) { double n = get_number(space_val); if(n >= 1) { int i, len; if(n > 10) n = 10; len = floor(n); for(i=0; i < len; i++) stringify_ctx.gap[i] = ' '; stringify_ctx.gap[len] = 0; } }else if(is_string(space_val)) { jsstr_t *space_str = get_string(space_val); size_t len = jsstr_length(space_str); if(len > 10) len = 10; jsstr_extract(space_str, 0, len, stringify_ctx.gap); } jsval_release(space_val); } hres = stringify(&stringify_ctx, argv[0]); if(SUCCEEDED(hres) && r) { assert(!stringify_ctx.stack_top); if(hres == S_OK) { jsstr_t *ret = jsstr_alloc_len(stringify_ctx.buf, stringify_ctx.buf_len); if(ret) *r = jsval_string(ret); else hres = E_OUTOFMEMORY; }else { *r = jsval_undefined(); } } heap_free(stringify_ctx.buf); heap_free(stringify_ctx.stack); return hres; }
static HRESULT invoke_source(script_ctx_t *ctx, FunctionInstance *function, IDispatch *this_obj, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *var_disp, *arg_disp; exec_ctx_t *exec_ctx; scope_chain_t *scope; HRESULT hres; if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) { WARN("Script engine state does not allow running code.\n"); return E_UNEXPECTED; } if(!function->func_code) { FIXME("no source\n"); return E_FAIL; } hres = create_var_disp(ctx, function, argc, argv, &var_disp); if(FAILED(hres)) return hres; hres = create_arguments(ctx, function, var_disp, argc, argv, &arg_disp); if(FAILED(hres)) { jsdisp_release(var_disp); return hres; } hres = jsdisp_propput(var_disp, argumentsW, PROPF_DONTDELETE, jsval_obj(arg_disp)); if(FAILED(hres)) { jsdisp_release(arg_disp); jsdisp_release(var_disp); return hres; } hres = scope_push(function->scope_chain, var_disp, to_disp(var_disp), &scope); if(SUCCEEDED(hres)) { hres = create_exec_ctx(ctx, this_obj, var_disp, scope, FALSE, &exec_ctx); scope_release(scope); if(SUCCEEDED(hres)) { jsdisp_t *prev_args; prev_args = function->arguments; function->arguments = arg_disp; hres = exec_source(exec_ctx, function->code, function->func_code, FALSE, r); function->arguments = prev_args; exec_release(exec_ctx); } } /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have * their own arguments property, it's impossible to use prototype's one during name lookup */ jsdisp_propput_name(var_disp, argumentsW, jsval_undefined()); jsdisp_release(arg_disp); jsdisp_release(var_disp); return hres; }
static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret) { jsval_t *argv, val; DWORD length, i; HRESULT hres; hres = jsdisp_propget_name(arg_array, lengthW, &val); if(FAILED(hres)) return hres; hres = to_uint32(ctx, val, &length); jsval_release(val); if(FAILED(hres)) return hres; argv = heap_alloc(length * sizeof(*argv)); if(!argv) return E_OUTOFMEMORY; for(i=0; i<length; i++) { hres = jsdisp_get_idx(arg_array, i, argv+i); if(hres == DISP_E_UNKNOWNNAME) { argv[i] = jsval_undefined(); }else if(FAILED(hres)) { while(i--) jsval_release(argv[i]); heap_free(argv); return hres; } } *argc = length; *ret = argv; return S_OK; }
static HRESULT Enumerator_moveFirst(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { EnumeratorInstance *This; HRESULT hres = S_OK; TRACE("\n"); if (!(This = enumerator_this(jsthis))) return throw_type_error(ctx, JS_E_ENUMERATOR_EXPECTED, NULL); if (This->enumvar) { hres = IEnumVARIANT_Reset(This->enumvar); if (FAILED(hres)) return hres; This->atend = FALSE; hres = enumvar_get_next_item(This); if(FAILED(hres)) return hres; } if (r) *r = jsval_undefined(); return S_OK; }
HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) { switch(V_VT(var)) { case VT_EMPTY: *r = jsval_undefined(); return S_OK; case VT_NULL: *r = jsval_null(); return S_OK; case VT_BOOL: *r = jsval_bool(V_BOOL(var)); return S_OK; case VT_I4: *r = jsval_number(V_I4(var)); return S_OK; case VT_R8: *r = jsval_number(V_R8(var)); return S_OK; case VT_BSTR: { jsstr_t *str; str = jsstr_alloc_len(V_BSTR(var), SysStringLen(V_BSTR(var))); if(!str) return E_OUTOFMEMORY; if(!V_BSTR(var)) str->length_flags |= JSSTR_FLAG_NULLBSTR; *r = jsval_string(str); return S_OK; } case VT_DISPATCH: { if(V_DISPATCH(var)) IDispatch_AddRef(V_DISPATCH(var)); *r = jsval_disp(V_DISPATCH(var)); return S_OK; } case VT_I2: *r = jsval_number(V_I2(var)); return S_OK; case VT_INT: *r = jsval_number(V_INT(var)); return S_OK; case VT_UNKNOWN: if(V_UNKNOWN(var)) { IDispatch *disp; HRESULT hres; hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp); if(SUCCEEDED(hres)) { *r = jsval_disp(disp); return S_OK; } } /* fall through */ default: return jsval_variant(r, var); } }
/* ECMA-262 3rd Edition 15.4.4.13 */ static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsthis; WCHAR buf[14], *buf_end, *str; DWORD i, length; jsval_t val; DISPID id; HRESULT hres; TRACE("\n"); hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; if(argc) { buf_end = buf + sizeof(buf)/sizeof(WCHAR)-1; *buf_end-- = 0; i = length; while(i--) { str = idx_to_str(i, buf_end); hres = jsdisp_get_id(jsthis, str, 0, &id); if(SUCCEEDED(hres)) { hres = jsdisp_propget(jsthis, id, &val); if(FAILED(hres)) return hres; hres = jsdisp_propput_idx(jsthis, i+argc, val); jsval_release(val); }else if(hres == DISP_E_UNKNOWNNAME) { hres = IDispatchEx_DeleteMemberByDispID(vthis->u.dispex, id); } } if(FAILED(hres)) return hres; } for(i=0; i<argc; i++) { hres = jsdisp_propput_idx(jsthis, i, argv[i]); if(FAILED(hres)) return hres; } if(argc) { length += argc; hres = set_length(jsthis, length); if(FAILED(hres)) return hres; } if(r) *r = ctx->version < 2 ? jsval_undefined() : jsval_number(length); return S_OK; }
static HRESULT Array_indexOf(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsthis; unsigned length, i, from = 0; jsval_t search, value; BOOL eq; HRESULT hres; TRACE("\n"); hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; if(!length) { if(r) *r = jsval_number(-1); return S_OK; } search = argc ? argv[0] : jsval_undefined(); if(argc > 1) { double from_arg; hres = to_integer(ctx, argv[1], &from_arg); if(FAILED(hres)) return hres; if(from_arg >= 0) from = min(from_arg, length); else from = max(from_arg + length, 0); } for(i = from; i < length; i++) { hres = jsdisp_get_idx(jsthis, i, &value); if(hres == DISP_E_UNKNOWNNAME) continue; if(FAILED(hres)) return hres; hres = jsval_strict_equal(value, search, &eq); jsval_release(value); if(FAILED(hres)) return hres; if(eq) { if(r) *r = jsval_number(i); return S_OK; } } if(r) *r = jsval_number(-1); return S_OK; }
static HRESULT jsval_variant(jsval_t *val, VARIANT *var) { VARIANT *v; HRESULT hres; __JSVAL_TYPE(*val) = JSV_VARIANT; __JSVAL_VAR(*val) = v = heap_alloc(sizeof(VARIANT)); if(!v) { *val = jsval_undefined(); return E_OUTOFMEMORY; } V_VT(v) = VT_EMPTY; hres = VariantCopy(v, var); if(FAILED(hres)) { *val = jsval_undefined(); heap_free(v); } return hres; }
static HRESULT WINAPI JScriptParse_InitNew(IActiveScriptParse *iface) { JScript *This = impl_from_IActiveScriptParse(iface); script_ctx_t *ctx; HRESULT hres; TRACE("(%p)\n", This); if(This->ctx) return E_UNEXPECTED; ctx = heap_alloc_zero(sizeof(script_ctx_t)); if(!ctx) return E_OUTOFMEMORY; ctx->ref = 1; ctx->state = SCRIPTSTATE_UNINITIALIZED; ctx->active_script = &This->IActiveScript_iface; ctx->safeopt = This->safeopt; ctx->version = This->version; ctx->html_mode = This->html_mode; ctx->ei.val = jsval_undefined(); ctx->acc = jsval_undefined(); heap_pool_init(&ctx->tmp_heap); hres = create_jscaller(ctx); if(FAILED(hres)) { heap_free(ctx); return hres; } ctx->last_match = jsstr_empty(); ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); if(ctx) { script_release(ctx); return E_UNEXPECTED; } return This->site ? set_ctx_site(This) : S_OK; }
static HRESULT init_parameters(jsdisp_t *var_disp, FunctionInstance *function, unsigned argc, jsval_t *argv) { DWORD i=0; HRESULT hres; for(i=0; i < function->func_code->param_cnt; i++) { hres = jsdisp_propput_name(var_disp, function->func_code->params[i], i < argc ? argv[i] : jsval_undefined()); if(FAILED(hres)) return hres; } return S_OK; }
static HRESULT Array_forEach(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsval_t value, args[3], res; jsdisp_t *jsthis; unsigned length, i; HRESULT hres; TRACE("\n"); /* FIXME: Check IsCallable */ if(argc != 1 || !is_object_instance(argv[0])) { FIXME("Unsupported arguments\n"); return E_NOTIMPL; } hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; for(i = 0; i < length; i++) { hres = jsdisp_get_idx(jsthis, i, &value); if(hres == DISP_E_UNKNOWNNAME) continue; if(FAILED(hres)) return hres; args[0] = value; args[1] = jsval_number(i); args[2] = jsval_obj(jsthis); hres = disp_call_value(ctx, get_object(argv[0]), NULL, DISPATCH_METHOD, ARRAY_SIZE(args), args, &res); jsval_release(value); if(FAILED(hres)) return hres; jsval_release(res); } if(r) *r = jsval_undefined(); return S_OK; }
/* ECMA-262 3rd Edition 15.4.4.11 */ static HRESULT Array_sort(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *jsthis, *cmp_func = NULL; jsval_t *vtab, **sorttab = NULL; DWORD length; DWORD i; HRESULT hres = S_OK; TRACE("\n"); hres = get_length(ctx, vthis, &jsthis, &length); if(FAILED(hres)) return hres; if(argc > 1) { WARN("invalid arg_cnt %d\n", argc); return E_FAIL; } if(argc == 1) { if(!is_object_instance(argv[0])) { WARN("arg is not dispatch\n"); return E_FAIL; } cmp_func = iface_to_jsdisp((IUnknown*)get_object(argv[0])); if(!cmp_func || !is_class(cmp_func, JSCLASS_FUNCTION)) { WARN("cmp_func is not a function\n"); if(cmp_func) jsdisp_release(cmp_func); return E_FAIL; } } if(!length) { if(cmp_func) jsdisp_release(cmp_func); if(r) *r = jsval_obj(jsdisp_addref(jsthis)); return S_OK; } vtab = heap_alloc_zero(length * sizeof(*vtab)); if(vtab) { for(i=0; i<length; i++) { hres = jsdisp_get_idx(jsthis, i, vtab+i); if(hres == DISP_E_UNKNOWNNAME) { vtab[i] = jsval_undefined(); hres = S_OK; } else if(FAILED(hres)) { WARN("Could not get elem %d: %08x\n", i, hres); break; } } }else { hres = E_OUTOFMEMORY; } if(SUCCEEDED(hres)) { sorttab = heap_alloc(length*2*sizeof(*sorttab)); if(!sorttab) hres = E_OUTOFMEMORY; } /* merge-sort */ if(SUCCEEDED(hres)) { jsval_t *tmpv, **tmpbuf; INT cmp; tmpbuf = sorttab + length; for(i=0; i < length; i++) sorttab[i] = vtab+i; for(i=0; i < length/2; i++) { hres = sort_cmp(ctx, cmp_func, *sorttab[2*i+1], *sorttab[2*i], &cmp); if(FAILED(hres)) break; if(cmp < 0) { tmpv = sorttab[2*i]; sorttab[2*i] = sorttab[2*i+1]; sorttab[2*i+1] = tmpv; } } if(SUCCEEDED(hres)) { DWORD k, a, b, bend; for(k=2; k < length; k *= 2) { for(i=0; i+k < length; i += 2*k) { a = b = 0; if(i+2*k <= length) bend = k; else bend = length - (i+k); memcpy(tmpbuf, sorttab+i, k*sizeof(jsval_t*)); while(a < k && b < bend) { hres = sort_cmp(ctx, cmp_func, *tmpbuf[a], *sorttab[i+k+b], &cmp); if(FAILED(hres)) break; if(cmp < 0) { sorttab[i+a+b] = tmpbuf[a]; a++; }else { sorttab[i+a+b] = sorttab[i+k+b]; b++; } } if(FAILED(hres)) break; if(a < k) memcpy(sorttab+i+a+b, tmpbuf+a, (k-a)*sizeof(jsval_t*)); } if(FAILED(hres)) break; } } for(i=0; SUCCEEDED(hres) && i < length; i++) hres = jsdisp_propput_idx(jsthis, i, *sorttab[i]); } if(vtab) { for(i=0; i < length; i++) jsval_release(vtab[i]); heap_free(vtab); } heap_free(sorttab); if(cmp_func) jsdisp_release(cmp_func); if(FAILED(hres)) return hres; if(r) *r = jsval_obj(jsdisp_addref(jsthis)); return S_OK; }
HRESULT variant_to_jsval(VARIANT *var, jsval_t *r) { if(V_VT(var) == (VT_VARIANT|VT_BYREF)) var = V_VARIANTREF(var); switch(V_VT(var)) { case VT_EMPTY: *r = jsval_undefined(); return S_OK; case VT_NULL: *r = jsval_null(); return S_OK; case VT_BOOL: *r = jsval_bool(V_BOOL(var)); return S_OK; case VT_I4: *r = jsval_number(V_I4(var)); return S_OK; case VT_R8: *r = jsval_number(V_R8(var)); return S_OK; case VT_BSTR: { jsstr_t *str; if(V_BSTR(var)) { str = jsstr_alloc_len(V_BSTR(var), SysStringLen(V_BSTR(var))); if(!str) return E_OUTOFMEMORY; }else { str = jsstr_null_bstr(); } *r = jsval_string(str); return S_OK; } case VT_DISPATCH: { if(V_DISPATCH(var)) IDispatch_AddRef(V_DISPATCH(var)); *r = jsval_disp(V_DISPATCH(var)); return S_OK; } case VT_I2: *r = jsval_number(V_I2(var)); return S_OK; case VT_UI2: *r = jsval_number(V_UI2(var)); return S_OK; case VT_INT: *r = jsval_number(V_INT(var)); return S_OK; case VT_UI4: *r = jsval_number(V_UI4(var)); return S_OK; case VT_UI8: /* * Native doesn't support VT_UI8 here, but it's needed for IE9+ APIs * (native IE9 doesn't use jscript.dll for JavaScript). */ *r = jsval_number(V_UI8(var)); return S_OK; case VT_R4: *r = jsval_number(V_R4(var)); return S_OK; case VT_UNKNOWN: if(V_UNKNOWN(var)) { IDispatch *disp; HRESULT hres; hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp); if(SUCCEEDED(hres)) { *r = jsval_disp(disp); return S_OK; } }else { *r = jsval_disp(NULL); return S_OK; } /* fall through */ default: return jsval_variant(r, var); } }
/* ECMA-262 5.1 Edition 15.12.3 (abstract operation JO) */ static HRESULT stringify_object(stringify_ctx_t *ctx, jsdisp_t *obj) { DISPID dispid = DISPID_STARTENUM; jsval_t val = jsval_undefined(); unsigned prop_cnt = 0, i; size_t stepback; BSTR prop_name; HRESULT hres; if(is_on_stack(ctx, obj)) { FIXME("Found a cycle\n"); return E_FAIL; } if(!stringify_push_obj(ctx, obj)) return E_OUTOFMEMORY; if(!append_char(ctx, '{')) return E_OUTOFMEMORY; while((hres = IDispatchEx_GetNextDispID(&obj->IDispatchEx_iface, fdexEnumDefault, dispid, &dispid)) == S_OK) { jsval_release(val); hres = jsdisp_propget(obj, dispid, &val); if(FAILED(hres)) return hres; if(is_undefined(val)) continue; stepback = ctx->buf_len; if(prop_cnt && !append_char(ctx, ',')) { hres = E_OUTOFMEMORY; break; } if(*ctx->gap) { if(!append_char(ctx, '\n')) { hres = E_OUTOFMEMORY; break; } for(i=0; i < ctx->stack_top; i++) { if(!append_string(ctx, ctx->gap)) { hres = E_OUTOFMEMORY; break; } } } hres = IDispatchEx_GetMemberName(&obj->IDispatchEx_iface, dispid, &prop_name); if(FAILED(hres)) break; hres = json_quote(ctx, prop_name, SysStringLen(prop_name)); SysFreeString(prop_name); if(FAILED(hres)) break; if(!append_char(ctx, ':') || (*ctx->gap && !append_char(ctx, ' '))) { hres = E_OUTOFMEMORY; break; } hres = stringify(ctx, val); if(FAILED(hres)) break; if(hres == S_FALSE) { ctx->buf_len = stepback; continue; } prop_cnt++; } jsval_release(val); if(FAILED(hres)) return hres; if(prop_cnt && *ctx->gap) { if(!append_char(ctx, '\n')) return E_OUTOFMEMORY; for(i=1; i < ctx->stack_top; i++) { if(!append_string(ctx, ctx->gap)) { hres = E_OUTOFMEMORY; break; } } } if(!append_char(ctx, '}')) return E_OUTOFMEMORY; stringify_pop_obj(ctx); return S_OK; }