/* * Copyright 2009 Piotr Caban * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include "jscript.h" #include "engine.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(jscript); /* ECMA-262 3rd Edition 15.11.4.4 */ static HRESULT Error_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsstr_t *name = NULL, *msg = NULL, *ret = NULL; jsdisp_t *jsthis = NULL; jsval_t v; HRESULT hres; TRACE("\n"); if(is_object_instance(vthis)) jsthis = to_jsdisp(get_object(vthis)); else if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5) return JS_E_OBJECT_EXPECTED; if(!jsthis || ctx->version < 2) { if(r) { jsstr_t *str; str = jsstr_alloc(L"[object Error]"); if(!str) return E_OUTOFMEMORY; *r = jsval_string(str); } return S_OK; } hres = jsdisp_propget_name(jsthis, L"name", &v); if(FAILED(hres)) return hres; if(!is_undefined(v)) { hres = to_string(ctx, v, &name); jsval_release(v); if(FAILED(hres)) return hres; } hres = jsdisp_propget_name(jsthis, L"message", &v); if(SUCCEEDED(hres)) { if(!is_undefined(v)) { hres = to_string(ctx, v, &msg); jsval_release(v); } } if(SUCCEEDED(hres)) { unsigned name_len = name ? jsstr_length(name) : 0; unsigned msg_len = msg ? jsstr_length(msg) : 0; if(name_len && msg_len) { WCHAR *ptr; ret = jsstr_alloc_buf(name_len + msg_len + 2, &ptr); if(ret) { jsstr_flush(name, ptr); ptr[name_len] = ':'; ptr[name_len+1] = ' '; jsstr_flush(msg, ptr+name_len+2); }else { hres = E_OUTOFMEMORY; } }else if(name_len) { ret = name; name = NULL; }else if(msg_len) { ret = msg; msg = NULL; }else { ret = jsstr_alloc(L"[object Error]"); } } if(msg) jsstr_release(msg); if(name) jsstr_release(name); if(FAILED(hres)) return hres; if(!ret) return E_OUTOFMEMORY; if(r) *r = jsval_string(ret); else jsstr_release(ret); return S_OK; } static HRESULT Error_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); switch(flags) { case INVOKE_FUNC: return JS_E_FUNCTION_EXPECTED; default: FIXME("unimplemented flags %x\n", flags); return E_NOTIMPL; } return S_OK; } static const builtin_prop_t Error_props[] = { {L"toString", Error_toString, PROPF_METHOD} }; static const builtin_info_t Error_info = { JSCLASS_ERROR, Error_value, ARRAY_SIZE(Error_props), Error_props, NULL, NULL }; static const builtin_info_t ErrorInst_info = { JSCLASS_ERROR, Error_value, 0, NULL, NULL, NULL }; static HRESULT alloc_error(script_ctx_t *ctx, jsdisp_t *prototype, jsdisp_t *constr, jsdisp_t **ret) { jsdisp_t *err; HRESULT hres; err = calloc(1, sizeof(*err)); if(!err) return E_OUTOFMEMORY; if(prototype) hres = init_dispex(err, ctx, &Error_info, prototype); else hres = init_dispex_from_constr(err, ctx, &ErrorInst_info, constr ? constr : ctx->error_constr); if(FAILED(hres)) { free(err); return hres; } *ret = err; return S_OK; } static HRESULT create_error(script_ctx_t *ctx, jsdisp_t *constr, UINT number, jsstr_t *msg, jsdisp_t **ret) { jsdisp_t *err; HRESULT hres; hres = alloc_error(ctx, NULL, constr, &err); if(FAILED(hres)) return hres; hres = jsdisp_define_data_property(err, L"number", PROPF_WRITABLE | PROPF_CONFIGURABLE, jsval_number((INT)number)); if(FAILED(hres)) { jsdisp_release(err); return hres; } hres = jsdisp_define_data_property(err, L"message", PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, jsval_string(msg)); if(SUCCEEDED(hres)) hres = jsdisp_define_data_property(err, L"description", PROPF_WRITABLE | PROPF_CONFIGURABLE, jsval_string(msg)); if(FAILED(hres)) { jsdisp_release(err); return hres; } *ret = err; return S_OK; } static HRESULT error_constr(script_ctx_t *ctx, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r, jsdisp_t *constr) { jsdisp_t *err; UINT num = 0; jsstr_t *msg = NULL; HRESULT hres; if(argc) { double n; hres = to_number(ctx, argv[0], &n); if(FAILED(hres)) /* FIXME: really? */ n = NAN; if(isnan(n)) hres = to_string(ctx, argv[0], &msg); if(FAILED(hres)) return hres; num = n; } if(!msg) { if(argc > 1) { hres = to_string(ctx, argv[1], &msg); if(FAILED(hres)) return hres; }else { msg = jsstr_empty(); } } switch(flags) { case INVOKE_FUNC: case DISPATCH_CONSTRUCT: hres = create_error(ctx, constr, num, msg, &err); jsstr_release(msg); if(FAILED(hres)) return hres; if(r) *r = jsval_obj(err); else jsdisp_release(err); return S_OK; default: if(msg) jsstr_release(msg); FIXME("unimplemented flags %x\n", flags); return E_NOTIMPL; } } static HRESULT ErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->error_constr); } static HRESULT EvalErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->eval_error_constr); } static HRESULT RangeErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->range_error_constr); } static HRESULT ReferenceErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->reference_error_constr); } static HRESULT RegExpErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->regexp_error_constr); } static HRESULT SyntaxErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->syntax_error_constr); } static HRESULT TypeErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->type_error_constr); } static HRESULT URIErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { TRACE("\n"); return error_constr(ctx, flags, argc, argv, r, ctx->uri_error_constr); } HRESULT init_error_constr(script_ctx_t *ctx, jsdisp_t *object_prototype) { static const WCHAR *names[] = {L"Error", L"EvalError", L"RangeError", L"ReferenceError", L"RegExpError", L"SyntaxError", L"TypeError", L"URIError"}; jsdisp_t **constr_addr[] = {&ctx->error_constr, &ctx->eval_error_constr, &ctx->range_error_constr, &ctx->reference_error_constr, &ctx->regexp_error_constr, &ctx->syntax_error_constr, &ctx->type_error_constr, &ctx->uri_error_constr}; static builtin_invoke_t constr_val[] = {ErrorConstr_value, EvalErrorConstr_value, RangeErrorConstr_value, ReferenceErrorConstr_value, RegExpErrorConstr_value, SyntaxErrorConstr_value, TypeErrorConstr_value, URIErrorConstr_value}; jsdisp_t *err; unsigned int i; jsstr_t *str; HRESULT hres; for(i=0; i < ARRAY_SIZE(names); i++) { hres = alloc_error(ctx, i==0 ? object_prototype : NULL, NULL, &err); if(FAILED(hres)) return hres; str = jsstr_alloc(names[i]); if(!str) { jsdisp_release(err); return E_OUTOFMEMORY; } hres = jsdisp_define_data_property(err, L"name", PROPF_WRITABLE | PROPF_CONFIGURABLE, jsval_string(str)); jsstr_release(str); if(SUCCEEDED(hres)) hres = create_builtin_constructor(ctx, constr_val[i], names[i], NULL, PROPF_CONSTR|1, err, constr_addr[i]); jsdisp_release(err); if(FAILED(hres)) return hres; } return S_OK; } static jsstr_t *format_error_message(HRESULT error, const WCHAR *arg) { size_t len, arg_len = 0; const WCHAR *res, *pos; WCHAR *buf, *p; jsstr_t *r; if(!is_jscript_error(error)) return jsstr_empty(); len = LoadStringW(jscript_hinstance, HRESULT_CODE(error), (WCHAR*)&res, 0); pos = wmemchr(res, '|', len); if(pos && arg) arg_len = lstrlenW(arg); r = jsstr_alloc_buf(len + arg_len - (pos ? 1 : 0), &buf); if(!r) return jsstr_empty(); p = buf; if(pos > res) { memcpy(p, res, (pos - res) * sizeof(WCHAR)); p += pos - res; } pos = pos ? pos + 1 : res; if(arg_len) { memcpy(p, arg, arg_len * sizeof(WCHAR)); p += arg_len; } if(pos != res + len) memcpy(p, pos, (res + len - pos) * sizeof(WCHAR)); return r; } HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str) { jsexcept_t *ei = ctx->ei; TRACE("%08lx\n", error); reset_ei(ei); ei->error = error; if(str) ei->message = format_error_message(error, str); return DISP_E_EXCEPTION; } void set_error_location(jsexcept_t *ei, bytecode_t *code, unsigned loc, unsigned source_id, jsstr_t *line) { if(is_jscript_error(ei->error)) { if(!ei->source) { const WCHAR *res; size_t len; len = LoadStringW(jscript_hinstance, source_id, (WCHAR*)&res, 0); ei->source = jsstr_alloc_len(res, len); } if(!ei->message) ei->message = format_error_message(ei->error, NULL); } TRACE("source %s in %s\n", debugstr_w(code->source + loc), debugstr_w(code->source)); ei->code = bytecode_addref(code); ei->loc = loc; if(line) ei->line = jsstr_addref(line); } jsdisp_t *create_builtin_error(script_ctx_t *ctx) { jsdisp_t *constr = ctx->error_constr, *r; jsexcept_t *ei = ctx->ei; HRESULT hres; assert(FAILED(ei->error) && ei->error != DISP_E_EXCEPTION); if(is_jscript_error(ei->error)) { switch(ei->error) { case JS_E_SYNTAX: case JS_E_MISSING_SEMICOLON: case JS_E_MISSING_LBRACKET: case JS_E_MISSING_RBRACKET: case JS_E_EXPECTED_IDENTIFIER: case JS_E_EXPECTED_ASSIGN: case JS_E_INVALID_CHAR: case JS_E_UNTERMINATED_STRING: case JS_E_MISPLACED_RETURN: case JS_E_INVALID_BREAK: case JS_E_INVALID_CONTINUE: case JS_E_LABEL_REDEFINED: case JS_E_LABEL_NOT_FOUND: case JS_E_EXPECTED_CCEND: case JS_E_DISABLED_CC: case JS_E_EXPECTED_AT: constr = ctx->syntax_error_constr; break; case JS_E_TO_PRIMITIVE: case JS_E_INVALIDARG: case JS_E_OBJECT_REQUIRED: case JS_E_INVALID_PROPERTY: case JS_E_INVALID_ACTION: case JS_E_MISSING_ARG: case JS_E_FUNCTION_EXPECTED: case JS_E_DATE_EXPECTED: case JS_E_NUMBER_EXPECTED: case JS_E_OBJECT_EXPECTED: case JS_E_UNDEFINED_VARIABLE: case JS_E_BOOLEAN_EXPECTED: case JS_E_VBARRAY_EXPECTED: case JS_E_INVALID_DELETE: case JS_E_JSCRIPT_EXPECTED: case JS_E_ENUMERATOR_EXPECTED: case JS_E_REGEXP_EXPECTED: case JS_E_ARRAY_EXPECTED: case JS_E_CYCLIC_PROTO_VALUE: case JS_E_CANNOT_CREATE_FOR_NONEXTENSIBLE: case JS_E_OBJECT_NONEXTENSIBLE: case JS_E_NONCONFIGURABLE_REDEFINED: case JS_E_NONWRITABLE_MODIFIED: case JS_E_PROP_DESC_MISMATCH: case JS_E_INVALID_WRITABLE_PROP_DESC: constr = ctx->type_error_constr; break; case JS_E_SUBSCRIPT_OUT_OF_RANGE: case JS_E_FRACTION_DIGITS_OUT_OF_RANGE: case JS_E_PRECISION_OUT_OF_RANGE: case JS_E_INVALID_LENGTH: constr = ctx->range_error_constr; break; case JS_E_ILLEGAL_ASSIGN: constr = ctx->reference_error_constr; break; case JS_E_REGEXP_SYNTAX: constr = ctx->regexp_error_constr; break; case JS_E_INVALID_URI_CODING: case JS_E_INVALID_URI_CHAR: constr = ctx->uri_error_constr; break; } } hres = create_error(ctx, constr, ei->error, ei->message ? ei->message : jsstr_empty(), &r); return SUCCEEDED(hres) ? r : NULL; }