jscript: Factor out format_error_message implementation.
[wine.git] / dlls / jscript / error.c
blob7b35e166004a6696cc3d174c5eedf3717ef77c97
1 /*
2 * Copyright 2009 Piotr Caban
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 <math.h>
21 #include <assert.h>
22 #include <wchar.h>
24 #include "jscript.h"
25 #include "engine.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
31 static const WCHAR descriptionW[] = {'d','e','s','c','r','i','p','t','i','o','n',0};
32 static const WCHAR messageW[] = {'m','e','s','s','a','g','e',0};
33 static const WCHAR nameW[] = {'n','a','m','e',0};
34 static const WCHAR numberW[] = {'n','u','m','b','e','r',0};
35 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
37 /* ECMA-262 3rd Edition 15.11.4.4 */
38 static HRESULT Error_toString(script_ctx_t *ctx, vdisp_t *vthis, WORD flags,
39 unsigned argc, jsval_t *argv, jsval_t *r)
41 jsdisp_t *jsthis;
42 jsstr_t *name = NULL, *msg = NULL, *ret = NULL;
43 jsval_t v;
44 HRESULT hres;
46 static const WCHAR object_errorW[] = {'[','o','b','j','e','c','t',' ','E','r','r','o','r',']',0};
48 TRACE("\n");
50 jsthis = get_jsdisp(vthis);
51 if(!jsthis || ctx->version < 2) {
52 if(r) {
53 jsstr_t *str;
55 str = jsstr_alloc(object_errorW);
56 if(!str)
57 return E_OUTOFMEMORY;
58 *r = jsval_string(str);
60 return S_OK;
63 hres = jsdisp_propget_name(jsthis, nameW, &v);
64 if(FAILED(hres))
65 return hres;
67 if(!is_undefined(v)) {
68 hres = to_string(ctx, v, &name);
69 jsval_release(v);
70 if(FAILED(hres))
71 return hres;
74 hres = jsdisp_propget_name(jsthis, messageW, &v);
75 if(SUCCEEDED(hres)) {
76 if(!is_undefined(v)) {
77 hres = to_string(ctx, v, &msg);
78 jsval_release(v);
82 if(SUCCEEDED(hres)) {
83 unsigned name_len = name ? jsstr_length(name) : 0;
84 unsigned msg_len = msg ? jsstr_length(msg) : 0;
86 if(name_len && msg_len) {
87 WCHAR *ptr;
89 ret = jsstr_alloc_buf(name_len + msg_len + 2, &ptr);
90 if(ret) {
91 jsstr_flush(name, ptr);
92 ptr[name_len] = ':';
93 ptr[name_len+1] = ' ';
94 jsstr_flush(msg, ptr+name_len+2);
95 }else {
96 hres = E_OUTOFMEMORY;
98 }else if(name_len) {
99 ret = name;
100 name = NULL;
101 }else if(msg_len) {
102 ret = msg;
103 msg = NULL;
104 }else {
105 ret = jsstr_alloc(object_errorW);
109 if(msg)
110 jsstr_release(msg);
111 if(name)
112 jsstr_release(name);
113 if(FAILED(hres))
114 return hres;
115 if(!ret)
116 return E_OUTOFMEMORY;
118 if(r)
119 *r = jsval_string(ret);
120 else
121 jsstr_release(ret);
122 return S_OK;
125 static HRESULT Error_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
126 unsigned argc, jsval_t *argv, jsval_t *r)
128 TRACE("\n");
130 switch(flags) {
131 case INVOKE_FUNC:
132 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
133 default:
134 FIXME("unimplemented flags %x\n", flags);
135 return E_NOTIMPL;
138 return S_OK;
141 static const builtin_prop_t Error_props[] = {
142 {toStringW, Error_toString, PROPF_METHOD}
145 static const builtin_info_t Error_info = {
146 JSCLASS_ERROR,
147 {NULL, Error_value, 0},
148 ARRAY_SIZE(Error_props),
149 Error_props,
150 NULL,
151 NULL
154 static const builtin_info_t ErrorInst_info = {
155 JSCLASS_ERROR,
156 {NULL, Error_value, 0},
158 NULL,
159 NULL,
160 NULL
163 static HRESULT alloc_error(script_ctx_t *ctx, jsdisp_t *prototype,
164 jsdisp_t *constr, jsdisp_t **ret)
166 jsdisp_t *err;
167 HRESULT hres;
169 err = heap_alloc_zero(sizeof(*err));
170 if(!err)
171 return E_OUTOFMEMORY;
173 if(prototype)
174 hres = init_dispex(err, ctx, &Error_info, prototype);
175 else
176 hres = init_dispex_from_constr(err, ctx, &ErrorInst_info,
177 constr ? constr : ctx->error_constr);
178 if(FAILED(hres)) {
179 heap_free(err);
180 return hres;
183 *ret = err;
184 return S_OK;
187 static HRESULT create_error(script_ctx_t *ctx, jsdisp_t *constr,
188 UINT number, jsstr_t *msg, jsdisp_t **ret)
190 jsdisp_t *err;
191 HRESULT hres;
193 hres = alloc_error(ctx, NULL, constr, &err);
194 if(FAILED(hres))
195 return hres;
197 hres = jsdisp_define_data_property(err, numberW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
198 jsval_number((INT)number));
199 if(FAILED(hres)) {
200 jsdisp_release(err);
201 return hres;
204 hres = jsdisp_define_data_property(err, messageW,
205 PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE,
206 jsval_string(msg));
207 if(SUCCEEDED(hres))
208 hres = jsdisp_define_data_property(err, descriptionW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
209 jsval_string(msg));
210 if(FAILED(hres)) {
211 jsdisp_release(err);
212 return hres;
215 *ret = err;
216 return S_OK;
219 static HRESULT error_constr(script_ctx_t *ctx, WORD flags, unsigned argc, jsval_t *argv,
220 jsval_t *r, jsdisp_t *constr) {
221 jsdisp_t *err;
222 UINT num = 0;
223 jsstr_t *msg = NULL;
224 HRESULT hres;
226 if(argc) {
227 double n;
229 hres = to_number(ctx, argv[0], &n);
230 if(FAILED(hres)) /* FIXME: really? */
231 n = NAN;
232 if(isnan(n))
233 hres = to_string(ctx, argv[0], &msg);
234 if(FAILED(hres))
235 return hres;
236 num = n;
239 if(!msg) {
240 if(argc > 1) {
241 hres = to_string(ctx, argv[1], &msg);
242 if(FAILED(hres))
243 return hres;
244 }else {
245 msg = jsstr_empty();
249 switch(flags) {
250 case INVOKE_FUNC:
251 case DISPATCH_CONSTRUCT:
252 hres = create_error(ctx, constr, num, msg, &err);
253 jsstr_release(msg);
254 if(FAILED(hres))
255 return hres;
257 if(r)
258 *r = jsval_obj(err);
259 else
260 jsdisp_release(err);
261 return S_OK;
263 default:
264 if(msg)
265 jsstr_release(msg);
266 FIXME("unimplemented flags %x\n", flags);
267 return E_NOTIMPL;
271 static HRESULT ErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
272 unsigned argc, jsval_t *argv, jsval_t *r)
274 TRACE("\n");
275 return error_constr(ctx, flags, argc, argv, r, ctx->error_constr);
278 static HRESULT EvalErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
279 unsigned argc, jsval_t *argv, jsval_t *r)
281 TRACE("\n");
282 return error_constr(ctx, flags, argc, argv, r, ctx->eval_error_constr);
285 static HRESULT RangeErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
286 unsigned argc, jsval_t *argv, jsval_t *r)
288 TRACE("\n");
289 return error_constr(ctx, flags, argc, argv, r, ctx->range_error_constr);
292 static HRESULT ReferenceErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
293 unsigned argc, jsval_t *argv, jsval_t *r)
295 TRACE("\n");
296 return error_constr(ctx, flags, argc, argv, r, ctx->reference_error_constr);
299 static HRESULT RegExpErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
300 unsigned argc, jsval_t *argv, jsval_t *r)
302 TRACE("\n");
303 return error_constr(ctx, flags, argc, argv, r, ctx->regexp_error_constr);
306 static HRESULT SyntaxErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
307 unsigned argc, jsval_t *argv, jsval_t *r)
309 TRACE("\n");
310 return error_constr(ctx, flags, argc, argv, r, ctx->syntax_error_constr);
313 static HRESULT TypeErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
314 unsigned argc, jsval_t *argv, jsval_t *r)
316 TRACE("\n");
317 return error_constr(ctx, flags, argc, argv, r, ctx->type_error_constr);
320 static HRESULT URIErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
321 unsigned argc, jsval_t *argv, jsval_t *r)
323 TRACE("\n");
324 return error_constr(ctx, flags, argc, argv, r, ctx->uri_error_constr);
327 HRESULT init_error_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
329 static const WCHAR ErrorW[] = {'E','r','r','o','r',0};
330 static const WCHAR EvalErrorW[] = {'E','v','a','l','E','r','r','o','r',0};
331 static const WCHAR RangeErrorW[] = {'R','a','n','g','e','E','r','r','o','r',0};
332 static const WCHAR ReferenceErrorW[] = {'R','e','f','e','r','e','n','c','e','E','r','r','o','r',0};
333 static const WCHAR RegExpErrorW[] = {'R','e','g','E','x','p','E','r','r','o','r',0};
334 static const WCHAR SyntaxErrorW[] = {'S','y','n','t','a','x','E','r','r','o','r',0};
335 static const WCHAR TypeErrorW[] = {'T','y','p','e','E','r','r','o','r',0};
336 static const WCHAR URIErrorW[] = {'U','R','I','E','r','r','o','r',0};
337 static const WCHAR *names[] = {ErrorW, EvalErrorW, RangeErrorW,
338 ReferenceErrorW, RegExpErrorW, SyntaxErrorW, TypeErrorW, URIErrorW};
339 jsdisp_t **constr_addr[] = {&ctx->error_constr, &ctx->eval_error_constr,
340 &ctx->range_error_constr, &ctx->reference_error_constr, &ctx->regexp_error_constr,
341 &ctx->syntax_error_constr, &ctx->type_error_constr,
342 &ctx->uri_error_constr};
343 static builtin_invoke_t constr_val[] = {ErrorConstr_value, EvalErrorConstr_value,
344 RangeErrorConstr_value, ReferenceErrorConstr_value, RegExpErrorConstr_value,
345 SyntaxErrorConstr_value, TypeErrorConstr_value, URIErrorConstr_value};
347 jsdisp_t *err;
348 unsigned int i;
349 jsstr_t *str;
350 HRESULT hres;
352 for(i=0; i < ARRAY_SIZE(names); i++) {
353 hres = alloc_error(ctx, i==0 ? object_prototype : NULL, NULL, &err);
354 if(FAILED(hres))
355 return hres;
357 str = jsstr_alloc(names[i]);
358 if(!str) {
359 jsdisp_release(err);
360 return E_OUTOFMEMORY;
363 hres = jsdisp_define_data_property(err, nameW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
364 jsval_string(str));
365 jsstr_release(str);
366 if(SUCCEEDED(hres))
367 hres = create_builtin_constructor(ctx, constr_val[i], names[i], NULL,
368 PROPF_CONSTR|1, err, constr_addr[i]);
370 jsdisp_release(err);
371 if(FAILED(hres))
372 return hres;
375 return S_OK;
378 static jsstr_t *format_error_message(HRESULT error, const WCHAR *arg)
380 size_t len, arg_len = 0;
381 const WCHAR *res, *pos;
382 WCHAR *buf, *p;
383 jsstr_t *r;
385 if(!is_jscript_error(error))
386 return jsstr_empty();
388 len = LoadStringW(jscript_hinstance, HRESULT_CODE(error), (WCHAR*)&res, 0);
390 pos = wmemchr(res, '|', len);
391 if(pos && arg)
392 arg_len = lstrlenW(arg);
393 r = jsstr_alloc_buf(len + arg_len - (pos ? 1 : 0), &buf);
394 if(!r)
395 return jsstr_empty();
397 p = buf;
398 if(pos > res) {
399 memcpy(p, res, (pos - res) * sizeof(WCHAR));
400 p += pos - res;
402 pos = pos ? pos + 1 : res;
403 if(arg_len) {
404 memcpy(p, arg, arg_len * sizeof(WCHAR));
405 p += arg_len;
407 if(pos != res + len)
408 memcpy(p, pos, (res + len - pos) * sizeof(WCHAR));
409 return r;
412 static HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str, jsdisp_t *constr)
414 jsdisp_t *err;
415 jsstr_t *msg;
416 HRESULT hres;
418 if(!is_jscript_error(error))
419 return error;
421 msg = format_error_message(error, str);
422 if(!msg)
423 return E_OUTOFMEMORY;
425 WARN("%s\n", debugstr_jsstr(msg));
427 hres = create_error(ctx, constr, error, msg, &err);
428 jsstr_release(msg);
429 if(FAILED(hres))
430 return hres;
432 reset_ei(ctx->ei);
433 ctx->ei->valid_value = TRUE;
434 ctx->ei->value = jsval_obj(err);
435 return error;
438 HRESULT throw_generic_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
440 return throw_error(ctx, error, str, ctx->error_constr);
443 HRESULT throw_range_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
445 return throw_error(ctx, error, str, ctx->range_error_constr);
448 HRESULT throw_reference_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
450 return throw_error(ctx, error, str, ctx->reference_error_constr);
453 HRESULT throw_regexp_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
455 return throw_error(ctx, error, str, ctx->regexp_error_constr);
458 HRESULT throw_syntax_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
460 return throw_error(ctx, error, str, ctx->syntax_error_constr);
463 HRESULT throw_type_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
465 return throw_error(ctx, error, str, ctx->type_error_constr);
468 HRESULT throw_uri_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
470 return throw_error(ctx, error, str, ctx->uri_error_constr);
473 jsdisp_t *create_builtin_error(script_ctx_t *ctx)
475 jsdisp_t *constr = ctx->error_constr, *r;
476 jsexcept_t *ei = ctx->ei;
477 HRESULT hres;
479 assert(FAILED(ei->error) && ei->error != DISP_E_EXCEPTION);
481 if(is_jscript_error(ei->error)) {
482 switch(ei->error) {
483 case JS_E_SYNTAX:
484 case JS_E_MISSING_SEMICOLON:
485 case JS_E_MISSING_LBRACKET:
486 case JS_E_MISSING_RBRACKET:
487 case JS_E_EXPECTED_IDENTIFIER:
488 case JS_E_EXPECTED_ASSIGN:
489 case JS_E_INVALID_CHAR:
490 case JS_E_UNTERMINATED_STRING:
491 case JS_E_MISPLACED_RETURN:
492 case JS_E_INVALID_BREAK:
493 case JS_E_INVALID_CONTINUE:
494 case JS_E_LABEL_REDEFINED:
495 case JS_E_LABEL_NOT_FOUND:
496 case JS_E_EXPECTED_CCEND:
497 case JS_E_DISABLED_CC:
498 case JS_E_EXPECTED_AT:
499 constr = ctx->syntax_error_constr;
500 break;
502 case JS_E_TO_PRIMITIVE:
503 case JS_E_INVALIDARG:
504 case JS_E_OBJECT_REQUIRED:
505 case JS_E_INVALID_PROPERTY:
506 case JS_E_INVALID_ACTION:
507 case JS_E_MISSING_ARG:
508 case JS_E_FUNCTION_EXPECTED:
509 case JS_E_DATE_EXPECTED:
510 case JS_E_NUMBER_EXPECTED:
511 case JS_E_OBJECT_EXPECTED:
512 case JS_E_UNDEFINED_VARIABLE:
513 case JS_E_BOOLEAN_EXPECTED:
514 case JS_E_VBARRAY_EXPECTED:
515 case JS_E_INVALID_DELETE:
516 case JS_E_JSCRIPT_EXPECTED:
517 case JS_E_ENUMERATOR_EXPECTED:
518 case JS_E_ARRAY_EXPECTED:
519 case JS_E_NONCONFIGURABLE_REDEFINED:
520 case JS_E_NONWRITABLE_MODIFIED:
521 case JS_E_PROP_DESC_MISMATCH:
522 case JS_E_INVALID_WRITABLE_PROP_DESC:
523 constr = ctx->type_error_constr;
524 break;
526 case JS_E_SUBSCRIPT_OUT_OF_RANGE:
527 case JS_E_FRACTION_DIGITS_OUT_OF_RANGE:
528 case JS_E_PRECISION_OUT_OF_RANGE:
529 case JS_E_INVALID_LENGTH:
530 constr = ctx->range_error_constr;
531 break;
533 case JS_E_ILLEGAL_ASSIGN:
534 constr = ctx->reference_error_constr;
535 break;
537 case JS_E_REGEXP_SYNTAX:
538 constr = ctx->regexp_error_constr;
539 break;
541 case JS_E_INVALID_URI_CODING:
542 case JS_E_INVALID_URI_CHAR:
543 constr = ctx->uri_error_constr;
544 break;
548 hres = create_error(ctx, constr, ei->error, jsstr_empty(), &r);
549 return SUCCEEDED(hres) ? r : NULL;