mshtml: Fix enumerating first custom prop after builtins.
[wine.git] / dlls / jscript / error.c
blobd309d4207d3bf54a78257dc7b91103953e317b7c
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 /* ECMA-262 3rd Edition 15.11.4.4 */
32 static HRESULT Error_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags,
33 unsigned argc, jsval_t *argv, jsval_t *r)
35 jsstr_t *name = NULL, *msg = NULL, *ret = NULL;
36 jsdisp_t *jsthis = NULL;
37 jsval_t v;
38 HRESULT hres;
40 TRACE("\n");
42 if(is_object_instance(vthis))
43 jsthis = to_jsdisp(get_object(vthis));
44 else if(ctx->version >= SCRIPTLANGUAGEVERSION_ES5)
45 return JS_E_OBJECT_EXPECTED;
47 if(!jsthis || ctx->version < 2) {
48 if(r) {
49 jsstr_t *str;
51 str = jsstr_alloc(L"[object Error]");
52 if(!str)
53 return E_OUTOFMEMORY;
54 *r = jsval_string(str);
56 return S_OK;
59 hres = jsdisp_propget_name(jsthis, L"name", &v);
60 if(FAILED(hres))
61 return hres;
63 if(!is_undefined(v)) {
64 hres = to_string(ctx, v, &name);
65 jsval_release(v);
66 if(FAILED(hres))
67 return hres;
70 hres = jsdisp_propget_name(jsthis, L"message", &v);
71 if(SUCCEEDED(hres)) {
72 if(!is_undefined(v)) {
73 hres = to_string(ctx, v, &msg);
74 jsval_release(v);
78 if(SUCCEEDED(hres)) {
79 unsigned name_len = name ? jsstr_length(name) : 0;
80 unsigned msg_len = msg ? jsstr_length(msg) : 0;
82 if(name_len && msg_len) {
83 WCHAR *ptr;
85 ret = jsstr_alloc_buf(name_len + msg_len + 2, &ptr);
86 if(ret) {
87 jsstr_flush(name, ptr);
88 ptr[name_len] = ':';
89 ptr[name_len+1] = ' ';
90 jsstr_flush(msg, ptr+name_len+2);
91 }else {
92 hres = E_OUTOFMEMORY;
94 }else if(name_len) {
95 ret = name;
96 name = NULL;
97 }else if(msg_len) {
98 ret = msg;
99 msg = NULL;
100 }else {
101 ret = jsstr_alloc(L"[object Error]");
105 if(msg)
106 jsstr_release(msg);
107 if(name)
108 jsstr_release(name);
109 if(FAILED(hres))
110 return hres;
111 if(!ret)
112 return E_OUTOFMEMORY;
114 if(r)
115 *r = jsval_string(ret);
116 else
117 jsstr_release(ret);
118 return S_OK;
121 static HRESULT Error_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
122 unsigned argc, jsval_t *argv, jsval_t *r)
124 TRACE("\n");
126 switch(flags) {
127 case INVOKE_FUNC:
128 return JS_E_FUNCTION_EXPECTED;
129 default:
130 FIXME("unimplemented flags %x\n", flags);
131 return E_NOTIMPL;
134 return S_OK;
137 static const builtin_prop_t Error_props[] = {
138 {L"toString", Error_toString, PROPF_METHOD}
141 static const builtin_info_t Error_info = {
142 JSCLASS_ERROR,
143 Error_value,
144 ARRAY_SIZE(Error_props),
145 Error_props,
146 NULL,
147 NULL
150 static const builtin_info_t ErrorInst_info = {
151 JSCLASS_ERROR,
152 Error_value,
154 NULL,
155 NULL,
156 NULL
159 static HRESULT alloc_error(script_ctx_t *ctx, jsdisp_t *prototype,
160 jsdisp_t *constr, jsdisp_t **ret)
162 jsdisp_t *err;
163 HRESULT hres;
165 err = heap_alloc_zero(sizeof(*err));
166 if(!err)
167 return E_OUTOFMEMORY;
169 if(prototype)
170 hres = init_dispex(err, ctx, &Error_info, prototype);
171 else
172 hres = init_dispex_from_constr(err, ctx, &ErrorInst_info,
173 constr ? constr : ctx->error_constr);
174 if(FAILED(hres)) {
175 heap_free(err);
176 return hres;
179 *ret = err;
180 return S_OK;
183 static HRESULT create_error(script_ctx_t *ctx, jsdisp_t *constr,
184 UINT number, jsstr_t *msg, jsdisp_t **ret)
186 jsdisp_t *err;
187 HRESULT hres;
189 hres = alloc_error(ctx, NULL, constr, &err);
190 if(FAILED(hres))
191 return hres;
193 hres = jsdisp_define_data_property(err, L"number", PROPF_WRITABLE | PROPF_CONFIGURABLE,
194 jsval_number((INT)number));
195 if(FAILED(hres)) {
196 jsdisp_release(err);
197 return hres;
200 hres = jsdisp_define_data_property(err, L"message",
201 PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE,
202 jsval_string(msg));
203 if(SUCCEEDED(hres))
204 hres = jsdisp_define_data_property(err, L"description", PROPF_WRITABLE | PROPF_CONFIGURABLE,
205 jsval_string(msg));
206 if(FAILED(hres)) {
207 jsdisp_release(err);
208 return hres;
211 *ret = err;
212 return S_OK;
215 static HRESULT error_constr(script_ctx_t *ctx, WORD flags, unsigned argc, jsval_t *argv,
216 jsval_t *r, jsdisp_t *constr) {
217 jsdisp_t *err;
218 UINT num = 0;
219 jsstr_t *msg = NULL;
220 HRESULT hres;
222 if(argc) {
223 double n;
225 hres = to_number(ctx, argv[0], &n);
226 if(FAILED(hres)) /* FIXME: really? */
227 n = NAN;
228 if(isnan(n))
229 hres = to_string(ctx, argv[0], &msg);
230 if(FAILED(hres))
231 return hres;
232 num = n;
235 if(!msg) {
236 if(argc > 1) {
237 hres = to_string(ctx, argv[1], &msg);
238 if(FAILED(hres))
239 return hres;
240 }else {
241 msg = jsstr_empty();
245 switch(flags) {
246 case INVOKE_FUNC:
247 case DISPATCH_CONSTRUCT:
248 hres = create_error(ctx, constr, num, msg, &err);
249 jsstr_release(msg);
250 if(FAILED(hres))
251 return hres;
253 if(r)
254 *r = jsval_obj(err);
255 else
256 jsdisp_release(err);
257 return S_OK;
259 default:
260 if(msg)
261 jsstr_release(msg);
262 FIXME("unimplemented flags %x\n", flags);
263 return E_NOTIMPL;
267 static HRESULT ErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
268 unsigned argc, jsval_t *argv, jsval_t *r)
270 TRACE("\n");
271 return error_constr(ctx, flags, argc, argv, r, ctx->error_constr);
274 static HRESULT EvalErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
275 unsigned argc, jsval_t *argv, jsval_t *r)
277 TRACE("\n");
278 return error_constr(ctx, flags, argc, argv, r, ctx->eval_error_constr);
281 static HRESULT RangeErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
282 unsigned argc, jsval_t *argv, jsval_t *r)
284 TRACE("\n");
285 return error_constr(ctx, flags, argc, argv, r, ctx->range_error_constr);
288 static HRESULT ReferenceErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
289 unsigned argc, jsval_t *argv, jsval_t *r)
291 TRACE("\n");
292 return error_constr(ctx, flags, argc, argv, r, ctx->reference_error_constr);
295 static HRESULT RegExpErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
296 unsigned argc, jsval_t *argv, jsval_t *r)
298 TRACE("\n");
299 return error_constr(ctx, flags, argc, argv, r, ctx->regexp_error_constr);
302 static HRESULT SyntaxErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
303 unsigned argc, jsval_t *argv, jsval_t *r)
305 TRACE("\n");
306 return error_constr(ctx, flags, argc, argv, r, ctx->syntax_error_constr);
309 static HRESULT TypeErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
310 unsigned argc, jsval_t *argv, jsval_t *r)
312 TRACE("\n");
313 return error_constr(ctx, flags, argc, argv, r, ctx->type_error_constr);
316 static HRESULT URIErrorConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags,
317 unsigned argc, jsval_t *argv, jsval_t *r)
319 TRACE("\n");
320 return error_constr(ctx, flags, argc, argv, r, ctx->uri_error_constr);
323 HRESULT init_error_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
325 static const WCHAR *names[] = {L"Error", L"EvalError", L"RangeError",
326 L"ReferenceError", L"RegExpError", L"SyntaxError", L"TypeError", L"URIError"};
327 jsdisp_t **constr_addr[] = {&ctx->error_constr, &ctx->eval_error_constr,
328 &ctx->range_error_constr, &ctx->reference_error_constr, &ctx->regexp_error_constr,
329 &ctx->syntax_error_constr, &ctx->type_error_constr,
330 &ctx->uri_error_constr};
331 static builtin_invoke_t constr_val[] = {ErrorConstr_value, EvalErrorConstr_value,
332 RangeErrorConstr_value, ReferenceErrorConstr_value, RegExpErrorConstr_value,
333 SyntaxErrorConstr_value, TypeErrorConstr_value, URIErrorConstr_value};
335 jsdisp_t *err;
336 unsigned int i;
337 jsstr_t *str;
338 HRESULT hres;
340 for(i=0; i < ARRAY_SIZE(names); i++) {
341 hres = alloc_error(ctx, i==0 ? object_prototype : NULL, NULL, &err);
342 if(FAILED(hres))
343 return hres;
345 str = jsstr_alloc(names[i]);
346 if(!str) {
347 jsdisp_release(err);
348 return E_OUTOFMEMORY;
351 hres = jsdisp_define_data_property(err, L"name", PROPF_WRITABLE | PROPF_CONFIGURABLE,
352 jsval_string(str));
353 jsstr_release(str);
354 if(SUCCEEDED(hres))
355 hres = create_builtin_constructor(ctx, constr_val[i], names[i], NULL,
356 PROPF_CONSTR|1, err, constr_addr[i]);
358 jsdisp_release(err);
359 if(FAILED(hres))
360 return hres;
363 return S_OK;
366 static jsstr_t *format_error_message(HRESULT error, const WCHAR *arg)
368 size_t len, arg_len = 0;
369 const WCHAR *res, *pos;
370 WCHAR *buf, *p;
371 jsstr_t *r;
373 if(!is_jscript_error(error))
374 return jsstr_empty();
376 len = LoadStringW(jscript_hinstance, HRESULT_CODE(error), (WCHAR*)&res, 0);
378 pos = wmemchr(res, '|', len);
379 if(pos && arg)
380 arg_len = lstrlenW(arg);
381 r = jsstr_alloc_buf(len + arg_len - (pos ? 1 : 0), &buf);
382 if(!r)
383 return jsstr_empty();
385 p = buf;
386 if(pos > res) {
387 memcpy(p, res, (pos - res) * sizeof(WCHAR));
388 p += pos - res;
390 pos = pos ? pos + 1 : res;
391 if(arg_len) {
392 memcpy(p, arg, arg_len * sizeof(WCHAR));
393 p += arg_len;
395 if(pos != res + len)
396 memcpy(p, pos, (res + len - pos) * sizeof(WCHAR));
397 return r;
400 HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
402 jsexcept_t *ei = ctx->ei;
403 TRACE("%08lx\n", error);
404 reset_ei(ei);
405 ei->error = error;
406 if(str)
407 ei->message = format_error_message(error, str);
408 return DISP_E_EXCEPTION;
411 void set_error_location(jsexcept_t *ei, bytecode_t *code, unsigned loc, unsigned source_id, jsstr_t *line)
413 if(is_jscript_error(ei->error)) {
414 if(!ei->source) {
415 const WCHAR *res;
416 size_t len;
418 len = LoadStringW(jscript_hinstance, source_id, (WCHAR*)&res, 0);
419 ei->source = jsstr_alloc_len(res, len);
421 if(!ei->message)
422 ei->message = format_error_message(ei->error, NULL);
425 TRACE("source %s in %s\n", debugstr_w(code->source + loc), debugstr_w(code->source));
427 ei->code = bytecode_addref(code);
428 ei->loc = loc;
429 if(line)
430 ei->line = jsstr_addref(line);
433 jsdisp_t *create_builtin_error(script_ctx_t *ctx)
435 jsdisp_t *constr = ctx->error_constr, *r;
436 jsexcept_t *ei = ctx->ei;
437 HRESULT hres;
439 assert(FAILED(ei->error) && ei->error != DISP_E_EXCEPTION);
441 if(is_jscript_error(ei->error)) {
442 switch(ei->error) {
443 case JS_E_SYNTAX:
444 case JS_E_MISSING_SEMICOLON:
445 case JS_E_MISSING_LBRACKET:
446 case JS_E_MISSING_RBRACKET:
447 case JS_E_EXPECTED_IDENTIFIER:
448 case JS_E_EXPECTED_ASSIGN:
449 case JS_E_INVALID_CHAR:
450 case JS_E_UNTERMINATED_STRING:
451 case JS_E_MISPLACED_RETURN:
452 case JS_E_INVALID_BREAK:
453 case JS_E_INVALID_CONTINUE:
454 case JS_E_LABEL_REDEFINED:
455 case JS_E_LABEL_NOT_FOUND:
456 case JS_E_EXPECTED_CCEND:
457 case JS_E_DISABLED_CC:
458 case JS_E_EXPECTED_AT:
459 constr = ctx->syntax_error_constr;
460 break;
462 case JS_E_TO_PRIMITIVE:
463 case JS_E_INVALIDARG:
464 case JS_E_OBJECT_REQUIRED:
465 case JS_E_INVALID_PROPERTY:
466 case JS_E_INVALID_ACTION:
467 case JS_E_MISSING_ARG:
468 case JS_E_FUNCTION_EXPECTED:
469 case JS_E_DATE_EXPECTED:
470 case JS_E_NUMBER_EXPECTED:
471 case JS_E_OBJECT_EXPECTED:
472 case JS_E_UNDEFINED_VARIABLE:
473 case JS_E_BOOLEAN_EXPECTED:
474 case JS_E_VBARRAY_EXPECTED:
475 case JS_E_INVALID_DELETE:
476 case JS_E_JSCRIPT_EXPECTED:
477 case JS_E_ENUMERATOR_EXPECTED:
478 case JS_E_REGEXP_EXPECTED:
479 case JS_E_ARRAY_EXPECTED:
480 case JS_E_CYCLIC_PROTO_VALUE:
481 case JS_E_CANNOT_CREATE_FOR_NONEXTENSIBLE:
482 case JS_E_OBJECT_NONEXTENSIBLE:
483 case JS_E_NONCONFIGURABLE_REDEFINED:
484 case JS_E_NONWRITABLE_MODIFIED:
485 case JS_E_PROP_DESC_MISMATCH:
486 case JS_E_INVALID_WRITABLE_PROP_DESC:
487 constr = ctx->type_error_constr;
488 break;
490 case JS_E_SUBSCRIPT_OUT_OF_RANGE:
491 case JS_E_FRACTION_DIGITS_OUT_OF_RANGE:
492 case JS_E_PRECISION_OUT_OF_RANGE:
493 case JS_E_INVALID_LENGTH:
494 constr = ctx->range_error_constr;
495 break;
497 case JS_E_ILLEGAL_ASSIGN:
498 constr = ctx->reference_error_constr;
499 break;
501 case JS_E_REGEXP_SYNTAX:
502 constr = ctx->regexp_error_constr;
503 break;
505 case JS_E_INVALID_URI_CODING:
506 case JS_E_INVALID_URI_CHAR:
507 constr = ctx->uri_error_constr;
508 break;
512 hres = create_error(ctx, constr, ei->error, ei->message ? ei->message : jsstr_empty(), &r);
513 return SUCCEEDED(hres) ? r : NULL;