jscript: Support non-extensible objects in jsdisp_define_property.
[wine/zf.git] / dlls / jscript / error.c
blobbb9e5a28c8e75af2440da159c4f145822243516a
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, vdisp_t *vthis, WORD flags,
33 unsigned argc, jsval_t *argv, jsval_t *r)
35 jsdisp_t *jsthis;
36 jsstr_t *name = NULL, *msg = NULL, *ret = NULL;
37 jsval_t v;
38 HRESULT hres;
40 TRACE("\n");
42 jsthis = get_jsdisp(vthis);
43 if(!jsthis || ctx->version < 2) {
44 if(r) {
45 jsstr_t *str;
47 str = jsstr_alloc(L"[object Error]");
48 if(!str)
49 return E_OUTOFMEMORY;
50 *r = jsval_string(str);
52 return S_OK;
55 hres = jsdisp_propget_name(jsthis, L"name", &v);
56 if(FAILED(hres))
57 return hres;
59 if(!is_undefined(v)) {
60 hres = to_string(ctx, v, &name);
61 jsval_release(v);
62 if(FAILED(hres))
63 return hres;
66 hres = jsdisp_propget_name(jsthis, L"message", &v);
67 if(SUCCEEDED(hres)) {
68 if(!is_undefined(v)) {
69 hres = to_string(ctx, v, &msg);
70 jsval_release(v);
74 if(SUCCEEDED(hres)) {
75 unsigned name_len = name ? jsstr_length(name) : 0;
76 unsigned msg_len = msg ? jsstr_length(msg) : 0;
78 if(name_len && msg_len) {
79 WCHAR *ptr;
81 ret = jsstr_alloc_buf(name_len + msg_len + 2, &ptr);
82 if(ret) {
83 jsstr_flush(name, ptr);
84 ptr[name_len] = ':';
85 ptr[name_len+1] = ' ';
86 jsstr_flush(msg, ptr+name_len+2);
87 }else {
88 hres = E_OUTOFMEMORY;
90 }else if(name_len) {
91 ret = name;
92 name = NULL;
93 }else if(msg_len) {
94 ret = msg;
95 msg = NULL;
96 }else {
97 ret = jsstr_alloc(L"[object Error]");
101 if(msg)
102 jsstr_release(msg);
103 if(name)
104 jsstr_release(name);
105 if(FAILED(hres))
106 return hres;
107 if(!ret)
108 return E_OUTOFMEMORY;
110 if(r)
111 *r = jsval_string(ret);
112 else
113 jsstr_release(ret);
114 return S_OK;
117 static HRESULT Error_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
118 unsigned argc, jsval_t *argv, jsval_t *r)
120 TRACE("\n");
122 switch(flags) {
123 case INVOKE_FUNC:
124 return JS_E_FUNCTION_EXPECTED;
125 default:
126 FIXME("unimplemented flags %x\n", flags);
127 return E_NOTIMPL;
130 return S_OK;
133 static const builtin_prop_t Error_props[] = {
134 {L"toString", Error_toString, PROPF_METHOD}
137 static const builtin_info_t Error_info = {
138 JSCLASS_ERROR,
139 {NULL, Error_value, 0},
140 ARRAY_SIZE(Error_props),
141 Error_props,
142 NULL,
143 NULL
146 static const builtin_info_t ErrorInst_info = {
147 JSCLASS_ERROR,
148 {NULL, Error_value, 0},
150 NULL,
151 NULL,
152 NULL
155 static HRESULT alloc_error(script_ctx_t *ctx, jsdisp_t *prototype,
156 jsdisp_t *constr, jsdisp_t **ret)
158 jsdisp_t *err;
159 HRESULT hres;
161 err = heap_alloc_zero(sizeof(*err));
162 if(!err)
163 return E_OUTOFMEMORY;
165 if(prototype)
166 hres = init_dispex(err, ctx, &Error_info, prototype);
167 else
168 hres = init_dispex_from_constr(err, ctx, &ErrorInst_info,
169 constr ? constr : ctx->error_constr);
170 if(FAILED(hres)) {
171 heap_free(err);
172 return hres;
175 *ret = err;
176 return S_OK;
179 static HRESULT create_error(script_ctx_t *ctx, jsdisp_t *constr,
180 UINT number, jsstr_t *msg, jsdisp_t **ret)
182 jsdisp_t *err;
183 HRESULT hres;
185 hres = alloc_error(ctx, NULL, constr, &err);
186 if(FAILED(hres))
187 return hres;
189 hres = jsdisp_define_data_property(err, L"number", PROPF_WRITABLE | PROPF_CONFIGURABLE,
190 jsval_number((INT)number));
191 if(FAILED(hres)) {
192 jsdisp_release(err);
193 return hres;
196 hres = jsdisp_define_data_property(err, L"message",
197 PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE,
198 jsval_string(msg));
199 if(SUCCEEDED(hres))
200 hres = jsdisp_define_data_property(err, L"description", PROPF_WRITABLE | PROPF_CONFIGURABLE,
201 jsval_string(msg));
202 if(FAILED(hres)) {
203 jsdisp_release(err);
204 return hres;
207 *ret = err;
208 return S_OK;
211 static HRESULT error_constr(script_ctx_t *ctx, WORD flags, unsigned argc, jsval_t *argv,
212 jsval_t *r, jsdisp_t *constr) {
213 jsdisp_t *err;
214 UINT num = 0;
215 jsstr_t *msg = NULL;
216 HRESULT hres;
218 if(argc) {
219 double n;
221 hres = to_number(ctx, argv[0], &n);
222 if(FAILED(hres)) /* FIXME: really? */
223 n = NAN;
224 if(isnan(n))
225 hres = to_string(ctx, argv[0], &msg);
226 if(FAILED(hres))
227 return hres;
228 num = n;
231 if(!msg) {
232 if(argc > 1) {
233 hres = to_string(ctx, argv[1], &msg);
234 if(FAILED(hres))
235 return hres;
236 }else {
237 msg = jsstr_empty();
241 switch(flags) {
242 case INVOKE_FUNC:
243 case DISPATCH_CONSTRUCT:
244 hres = create_error(ctx, constr, num, msg, &err);
245 jsstr_release(msg);
246 if(FAILED(hres))
247 return hres;
249 if(r)
250 *r = jsval_obj(err);
251 else
252 jsdisp_release(err);
253 return S_OK;
255 default:
256 if(msg)
257 jsstr_release(msg);
258 FIXME("unimplemented flags %x\n", flags);
259 return E_NOTIMPL;
263 static HRESULT ErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
264 unsigned argc, jsval_t *argv, jsval_t *r)
266 TRACE("\n");
267 return error_constr(ctx, flags, argc, argv, r, ctx->error_constr);
270 static HRESULT EvalErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
271 unsigned argc, jsval_t *argv, jsval_t *r)
273 TRACE("\n");
274 return error_constr(ctx, flags, argc, argv, r, ctx->eval_error_constr);
277 static HRESULT RangeErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
278 unsigned argc, jsval_t *argv, jsval_t *r)
280 TRACE("\n");
281 return error_constr(ctx, flags, argc, argv, r, ctx->range_error_constr);
284 static HRESULT ReferenceErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
285 unsigned argc, jsval_t *argv, jsval_t *r)
287 TRACE("\n");
288 return error_constr(ctx, flags, argc, argv, r, ctx->reference_error_constr);
291 static HRESULT RegExpErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
292 unsigned argc, jsval_t *argv, jsval_t *r)
294 TRACE("\n");
295 return error_constr(ctx, flags, argc, argv, r, ctx->regexp_error_constr);
298 static HRESULT SyntaxErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
299 unsigned argc, jsval_t *argv, jsval_t *r)
301 TRACE("\n");
302 return error_constr(ctx, flags, argc, argv, r, ctx->syntax_error_constr);
305 static HRESULT TypeErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
306 unsigned argc, jsval_t *argv, jsval_t *r)
308 TRACE("\n");
309 return error_constr(ctx, flags, argc, argv, r, ctx->type_error_constr);
312 static HRESULT URIErrorConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
313 unsigned argc, jsval_t *argv, jsval_t *r)
315 TRACE("\n");
316 return error_constr(ctx, flags, argc, argv, r, ctx->uri_error_constr);
319 HRESULT init_error_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
321 static const WCHAR *names[] = {L"Error", L"EvalError", L"RangeError",
322 L"ReferenceError", L"RegExpError", L"SyntaxError", L"TypeError", L"URIError"};
323 jsdisp_t **constr_addr[] = {&ctx->error_constr, &ctx->eval_error_constr,
324 &ctx->range_error_constr, &ctx->reference_error_constr, &ctx->regexp_error_constr,
325 &ctx->syntax_error_constr, &ctx->type_error_constr,
326 &ctx->uri_error_constr};
327 static builtin_invoke_t constr_val[] = {ErrorConstr_value, EvalErrorConstr_value,
328 RangeErrorConstr_value, ReferenceErrorConstr_value, RegExpErrorConstr_value,
329 SyntaxErrorConstr_value, TypeErrorConstr_value, URIErrorConstr_value};
331 jsdisp_t *err;
332 unsigned int i;
333 jsstr_t *str;
334 HRESULT hres;
336 for(i=0; i < ARRAY_SIZE(names); i++) {
337 hres = alloc_error(ctx, i==0 ? object_prototype : NULL, NULL, &err);
338 if(FAILED(hres))
339 return hres;
341 str = jsstr_alloc(names[i]);
342 if(!str) {
343 jsdisp_release(err);
344 return E_OUTOFMEMORY;
347 hres = jsdisp_define_data_property(err, L"name", PROPF_WRITABLE | PROPF_CONFIGURABLE,
348 jsval_string(str));
349 jsstr_release(str);
350 if(SUCCEEDED(hres))
351 hres = create_builtin_constructor(ctx, constr_val[i], names[i], NULL,
352 PROPF_CONSTR|1, err, constr_addr[i]);
354 jsdisp_release(err);
355 if(FAILED(hres))
356 return hres;
359 return S_OK;
362 static jsstr_t *format_error_message(HRESULT error, const WCHAR *arg)
364 size_t len, arg_len = 0;
365 const WCHAR *res, *pos;
366 WCHAR *buf, *p;
367 jsstr_t *r;
369 if(!is_jscript_error(error))
370 return jsstr_empty();
372 len = LoadStringW(jscript_hinstance, HRESULT_CODE(error), (WCHAR*)&res, 0);
374 pos = wmemchr(res, '|', len);
375 if(pos && arg)
376 arg_len = lstrlenW(arg);
377 r = jsstr_alloc_buf(len + arg_len - (pos ? 1 : 0), &buf);
378 if(!r)
379 return jsstr_empty();
381 p = buf;
382 if(pos > res) {
383 memcpy(p, res, (pos - res) * sizeof(WCHAR));
384 p += pos - res;
386 pos = pos ? pos + 1 : res;
387 if(arg_len) {
388 memcpy(p, arg, arg_len * sizeof(WCHAR));
389 p += arg_len;
391 if(pos != res + len)
392 memcpy(p, pos, (res + len - pos) * sizeof(WCHAR));
393 return r;
396 HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *str)
398 jsexcept_t *ei = ctx->ei;
399 TRACE("%08x\n", error);
400 reset_ei(ei);
401 ei->error = error;
402 if(str)
403 ei->message = format_error_message(error, str);
404 return DISP_E_EXCEPTION;
407 void set_error_location(jsexcept_t *ei, bytecode_t *code, unsigned loc, unsigned source_id, jsstr_t *line)
409 if(is_jscript_error(ei->error)) {
410 if(!ei->source) {
411 const WCHAR *res;
412 size_t len;
414 len = LoadStringW(jscript_hinstance, source_id, (WCHAR*)&res, 0);
415 ei->source = jsstr_alloc_len(res, len);
417 if(!ei->message)
418 ei->message = format_error_message(ei->error, NULL);
421 TRACE("source %s in %s\n", debugstr_w(code->source + loc), debugstr_w(code->source));
423 ei->code = bytecode_addref(code);
424 ei->loc = loc;
425 if(line)
426 ei->line = jsstr_addref(line);
429 jsdisp_t *create_builtin_error(script_ctx_t *ctx)
431 jsdisp_t *constr = ctx->error_constr, *r;
432 jsexcept_t *ei = ctx->ei;
433 HRESULT hres;
435 assert(FAILED(ei->error) && ei->error != DISP_E_EXCEPTION);
437 if(is_jscript_error(ei->error)) {
438 switch(ei->error) {
439 case JS_E_SYNTAX:
440 case JS_E_MISSING_SEMICOLON:
441 case JS_E_MISSING_LBRACKET:
442 case JS_E_MISSING_RBRACKET:
443 case JS_E_EXPECTED_IDENTIFIER:
444 case JS_E_EXPECTED_ASSIGN:
445 case JS_E_INVALID_CHAR:
446 case JS_E_UNTERMINATED_STRING:
447 case JS_E_MISPLACED_RETURN:
448 case JS_E_INVALID_BREAK:
449 case JS_E_INVALID_CONTINUE:
450 case JS_E_LABEL_REDEFINED:
451 case JS_E_LABEL_NOT_FOUND:
452 case JS_E_EXPECTED_CCEND:
453 case JS_E_DISABLED_CC:
454 case JS_E_EXPECTED_AT:
455 constr = ctx->syntax_error_constr;
456 break;
458 case JS_E_TO_PRIMITIVE:
459 case JS_E_INVALIDARG:
460 case JS_E_OBJECT_REQUIRED:
461 case JS_E_INVALID_PROPERTY:
462 case JS_E_INVALID_ACTION:
463 case JS_E_MISSING_ARG:
464 case JS_E_FUNCTION_EXPECTED:
465 case JS_E_DATE_EXPECTED:
466 case JS_E_NUMBER_EXPECTED:
467 case JS_E_OBJECT_EXPECTED:
468 case JS_E_UNDEFINED_VARIABLE:
469 case JS_E_BOOLEAN_EXPECTED:
470 case JS_E_VBARRAY_EXPECTED:
471 case JS_E_INVALID_DELETE:
472 case JS_E_JSCRIPT_EXPECTED:
473 case JS_E_ENUMERATOR_EXPECTED:
474 case JS_E_REGEXP_EXPECTED:
475 case JS_E_ARRAY_EXPECTED:
476 case JS_E_OBJECT_NONEXTENSIBLE:
477 case JS_E_NONCONFIGURABLE_REDEFINED:
478 case JS_E_NONWRITABLE_MODIFIED:
479 case JS_E_PROP_DESC_MISMATCH:
480 case JS_E_INVALID_WRITABLE_PROP_DESC:
481 constr = ctx->type_error_constr;
482 break;
484 case JS_E_SUBSCRIPT_OUT_OF_RANGE:
485 case JS_E_FRACTION_DIGITS_OUT_OF_RANGE:
486 case JS_E_PRECISION_OUT_OF_RANGE:
487 case JS_E_INVALID_LENGTH:
488 constr = ctx->range_error_constr;
489 break;
491 case JS_E_ILLEGAL_ASSIGN:
492 constr = ctx->reference_error_constr;
493 break;
495 case JS_E_REGEXP_SYNTAX:
496 constr = ctx->regexp_error_constr;
497 break;
499 case JS_E_INVALID_URI_CODING:
500 case JS_E_INVALID_URI_CHAR:
501 constr = ctx->uri_error_constr;
502 break;
506 hres = create_error(ctx, constr, ei->error, ei->message ? ei->message : jsstr_empty(), &r);
507 return SUCCEEDED(hres) ? r : NULL;