[NTOSKRNL] Drop the useless Timestamp field
[reactos.git] / dll / win32 / jscript / object.c
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include <assert.h>
20
21 #include "jscript.h"
22
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
26
27 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
28 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
29 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
30 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
31 static const WCHAR propertyIsEnumerableW[] =
32 {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
33 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
34
35 static const WCHAR getOwnPropertyDescriptorW[] =
36 {'g','e','t','O','w','n','P','r','o','p','e','r','t','y','D','e','s','c','r','i','p','t','o','r',0};
37 static const WCHAR definePropertyW[] = {'d','e','f','i','n','e','P','r','o','p','e','r','t','y',0};
38
39 static const WCHAR definePropertiesW[] = {'d','e','f','i','n','e','P','r','o','p','e','r','t','i','e','s',0};
40
41 static const WCHAR default_valueW[] = {'[','o','b','j','e','c','t',' ','O','b','j','e','c','t',']',0};
42
43 static const WCHAR configurableW[] = {'c','o','n','f','i','g','u','r','a','b','l','e',0};
44 static const WCHAR enumerableW[] = {'e','n','u','m','e','r','a','b','l','e',0};
45 static const WCHAR valueW[] = {'v','a','l','u','e',0};
46 static const WCHAR writableW[] = {'w','r','i','t','a','b','l','e',0};
47 static const WCHAR getW[] = {'g','e','t',0};
48 static const WCHAR setW[] = {'s','e','t',0};
49
50 static HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
51 jsval_t *r)
52 {
53 jsdisp_t *jsdisp;
54 const WCHAR *str;
55
56 static const WCHAR formatW[] = {'[','o','b','j','e','c','t',' ','%','s',']',0};
57
58 static const WCHAR arrayW[] = {'A','r','r','a','y',0};
59 static const WCHAR booleanW[] = {'B','o','o','l','e','a','n',0};
60 static const WCHAR dateW[] = {'D','a','t','e',0};
61 static const WCHAR errorW[] = {'E','r','r','o','r',0};
62 static const WCHAR functionW[] = {'F','u','n','c','t','i','o','n',0};
63 static const WCHAR mathW[] = {'M','a','t','h',0};
64 static const WCHAR numberW[] = {'N','u','m','b','e','r',0};
65 static const WCHAR objectW[] = {'O','b','j','e','c','t',0};
66 static const WCHAR regexpW[] = {'R','e','g','E','x','p',0};
67 static const WCHAR stringW[] = {'S','t','r','i','n','g',0};
68 /* Keep in sync with jsclass_t enum */
69 static const WCHAR *names[] = {NULL, arrayW, booleanW, dateW, errorW,
70 functionW, NULL, mathW, numberW, objectW, regexpW, stringW, objectW, objectW, objectW};
71
72 TRACE("\n");
73
74 jsdisp = get_jsdisp(jsthis);
75 if(!jsdisp) {
76 str = objectW;
77 }else if(names[jsdisp->builtin_info->class]) {
78 str = names[jsdisp->builtin_info->class];
79 }else {
80 assert(jsdisp->builtin_info->class != JSCLASS_NONE);
81 FIXME("jdisp->builtin_info->class = %d\n", jsdisp->builtin_info->class);
82 return E_FAIL;
83 }
84
85 if(r) {
86 jsstr_t *ret;
87 WCHAR *ptr;
88
89 ret = jsstr_alloc_buf(9+strlenW(str), &ptr);
90 if(!ret)
91 return E_OUTOFMEMORY;
92
93 sprintfW(ptr, formatW, str);
94 *r = jsval_string(ret);
95 }
96
97 return S_OK;
98 }
99
100 static HRESULT Object_toLocaleString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
101 jsval_t *r)
102 {
103 TRACE("\n");
104
105 if(!is_jsdisp(jsthis)) {
106 FIXME("Host object this\n");
107 return E_FAIL;
108 }
109
110 return jsdisp_call_name(jsthis->u.jsdisp, toStringW, DISPATCH_METHOD, 0, NULL, r);
111 }
112
113 static HRESULT Object_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
114 jsval_t *r)
115 {
116 TRACE("\n");
117
118 if(r) {
119 IDispatch_AddRef(jsthis->u.disp);
120 *r = jsval_disp(jsthis->u.disp);
121 }
122 return S_OK;
123 }
124
125 static HRESULT Object_hasOwnProperty(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
126 jsval_t *r)
127 {
128 jsstr_t *name;
129 DISPID id;
130 BSTR bstr;
131 HRESULT hres;
132
133 TRACE("\n");
134
135 if(!argc) {
136 if(r)
137 *r = jsval_bool(FALSE);
138 return S_OK;
139 }
140
141 hres = to_string(ctx, argv[0], &name);
142 if(FAILED(hres))
143 return hres;
144
145 if(is_jsdisp(jsthis)) {
146 property_desc_t prop_desc;
147 const WCHAR *name_str;
148
149 name_str = jsstr_flatten(name);
150 if(!name_str) {
151 jsstr_release(name);
152 return E_OUTOFMEMORY;
153 }
154
155 hres = jsdisp_get_own_property(jsthis->u.jsdisp, name_str, TRUE, &prop_desc);
156 jsstr_release(name);
157 if(FAILED(hres) && hres != DISP_E_UNKNOWNNAME)
158 return hres;
159
160 if(r) *r = jsval_bool(hres == S_OK);
161 return S_OK;
162 }
163
164
165 bstr = SysAllocStringLen(NULL, jsstr_length(name));
166 if(bstr)
167 jsstr_flush(name, bstr);
168 jsstr_release(name);
169 if(!bstr)
170 return E_OUTOFMEMORY;
171
172 if(is_dispex(jsthis))
173 hres = IDispatchEx_GetDispID(jsthis->u.dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id);
174 else
175 hres = IDispatch_GetIDsOfNames(jsthis->u.disp, &IID_NULL, &bstr, 1, ctx->lcid, &id);
176
177 SysFreeString(bstr);
178 if(r)
179 *r = jsval_bool(SUCCEEDED(hres));
180 return S_OK;
181 }
182
183 static HRESULT Object_propertyIsEnumerable(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
184 jsval_t *r)
185 {
186 property_desc_t prop_desc;
187 const WCHAR *name;
188 jsstr_t *name_str;
189 HRESULT hres;
190
191 TRACE("\n");
192
193 if(argc != 1) {
194 FIXME("argc %d not supported\n", argc);
195 return E_NOTIMPL;
196 }
197
198 if(!is_jsdisp(jsthis)) {
199 FIXME("Host object this\n");
200 return E_FAIL;
201 }
202
203 hres = to_flat_string(ctx, argv[0], &name_str, &name);
204 if(FAILED(hres))
205 return hres;
206
207 hres = jsdisp_get_own_property(jsthis->u.jsdisp, name, TRUE, &prop_desc);
208 jsstr_release(name_str);
209 if(FAILED(hres) && hres != DISP_E_UNKNOWNNAME)
210 return hres;
211
212 if(r)
213 *r = jsval_bool(hres == S_OK && (prop_desc.flags & PROPF_ENUMERABLE) != 0);
214 return S_OK;
215 }
216
217 static HRESULT Object_isPrototypeOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
218 jsval_t *r)
219 {
220 FIXME("\n");
221 return E_NOTIMPL;
222 }
223
224 static HRESULT Object_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
225 {
226 jsstr_t *ret;
227
228 TRACE("\n");
229
230 ret = jsstr_alloc(default_valueW);
231 if(!ret)
232 return E_OUTOFMEMORY;
233
234 *r = jsval_string(ret);
235 return S_OK;
236 }
237
238 static void Object_destructor(jsdisp_t *dispex)
239 {
240 heap_free(dispex);
241 }
242
243 static const builtin_prop_t Object_props[] = {
244 {hasOwnPropertyW, Object_hasOwnProperty, PROPF_METHOD|1},
245 {isPrototypeOfW, Object_isPrototypeOf, PROPF_METHOD|1},
246 {propertyIsEnumerableW, Object_propertyIsEnumerable, PROPF_METHOD|1},
247 {toLocaleStringW, Object_toLocaleString, PROPF_METHOD},
248 {toStringW, Object_toString, PROPF_METHOD},
249 {valueOfW, Object_valueOf, PROPF_METHOD}
250 };
251
252 static const builtin_info_t Object_info = {
253 JSCLASS_OBJECT,
254 {NULL, NULL,0, Object_get_value},
255 ARRAY_SIZE(Object_props),
256 Object_props,
257 Object_destructor,
258 NULL
259 };
260
261 static const builtin_info_t ObjectInst_info = {
262 JSCLASS_OBJECT,
263 {NULL, NULL,0, Object_get_value},
264 0, NULL,
265 Object_destructor,
266 NULL
267 };
268
269 static void release_property_descriptor(property_desc_t *desc)
270 {
271 if(desc->explicit_value)
272 jsval_release(desc->value);
273 if(desc->getter)
274 jsdisp_release(desc->getter);
275 if(desc->setter)
276 jsdisp_release(desc->setter);
277 }
278
279 static HRESULT to_property_descriptor(script_ctx_t *ctx, jsdisp_t *attr_obj, property_desc_t *desc)
280 {
281 DISPID id;
282 jsval_t v;
283 BOOL b;
284 HRESULT hres;
285
286 memset(desc, 0, sizeof(*desc));
287 desc->value = jsval_undefined();
288
289 hres = jsdisp_get_id(attr_obj, enumerableW, 0, &id);
290 if(SUCCEEDED(hres)) {
291 desc->mask |= PROPF_ENUMERABLE;
292 hres = jsdisp_propget(attr_obj, id, &v);
293 if(FAILED(hres))
294 return hres;
295 hres = to_boolean(v, &b);
296 jsval_release(v);
297 if(FAILED(hres))
298 return hres;
299 if(b)
300 desc->flags |= PROPF_ENUMERABLE;
301 }else if(hres != DISP_E_UNKNOWNNAME) {
302 return hres;
303 }
304
305 hres = jsdisp_get_id(attr_obj, configurableW, 0, &id);
306 if(SUCCEEDED(hres)) {
307 desc->mask |= PROPF_CONFIGURABLE;
308 hres = jsdisp_propget(attr_obj, id, &v);
309 if(FAILED(hres))
310 return hres;
311 hres = to_boolean(v, &b);
312 jsval_release(v);
313 if(FAILED(hres))
314 return hres;
315 if(b)
316 desc->flags |= PROPF_CONFIGURABLE;
317 }else if(hres != DISP_E_UNKNOWNNAME) {
318 return hres;
319 }
320
321 hres = jsdisp_get_id(attr_obj, valueW, 0, &id);
322 if(SUCCEEDED(hres)) {
323 hres = jsdisp_propget(attr_obj, id, &desc->value);
324 if(FAILED(hres))
325 return hres;
326 desc->explicit_value = TRUE;
327 }else if(hres != DISP_E_UNKNOWNNAME) {
328 return hres;
329 }
330
331 hres = jsdisp_get_id(attr_obj, writableW, 0, &id);
332 if(SUCCEEDED(hres)) {
333 desc->mask |= PROPF_WRITABLE;
334 hres = jsdisp_propget(attr_obj, id, &v);
335 if(SUCCEEDED(hres)) {
336 hres = to_boolean(v, &b);
337 jsval_release(v);
338 if(SUCCEEDED(hres) && b)
339 desc->flags |= PROPF_WRITABLE;
340 }
341 }else if(hres == DISP_E_UNKNOWNNAME) {
342 hres = S_OK;
343 }
344 if(FAILED(hres)) {
345 release_property_descriptor(desc);
346 return hres;
347 }
348
349 hres = jsdisp_get_id(attr_obj, getW, 0, &id);
350 if(SUCCEEDED(hres)) {
351 desc->explicit_getter = TRUE;
352 hres = jsdisp_propget(attr_obj, id, &v);
353 if(SUCCEEDED(hres) && !is_undefined(v)) {
354 if(!is_object_instance(v)) {
355 FIXME("getter is not an object\n");
356 jsval_release(v);
357 hres = E_FAIL;
358 }else {
359 /* FIXME: Check IsCallable */
360 desc->getter = to_jsdisp(get_object(v));
361 if(!desc->getter)
362 FIXME("getter is not JS object\n");
363 }
364 }
365 }else if(hres == DISP_E_UNKNOWNNAME) {
366 hres = S_OK;
367 }
368 if(FAILED(hres)) {
369 release_property_descriptor(desc);
370 return hres;
371 }
372
373 hres = jsdisp_get_id(attr_obj, setW, 0, &id);
374 if(SUCCEEDED(hres)) {
375 desc->explicit_setter = TRUE;
376 hres = jsdisp_propget(attr_obj, id, &v);
377 if(SUCCEEDED(hres) && !is_undefined(v)) {
378 if(!is_object_instance(v)) {
379 FIXME("setter is not an object\n");
380 jsval_release(v);
381 hres = E_FAIL;
382 }else {
383 /* FIXME: Check IsCallable */
384 desc->setter = to_jsdisp(get_object(v));
385 if(!desc->setter)
386 FIXME("setter is not JS object\n");
387 }
388 }
389 }else if(hres == DISP_E_UNKNOWNNAME) {
390 hres = S_OK;
391 }
392 if(FAILED(hres)) {
393 release_property_descriptor(desc);
394 return hres;
395 }
396
397 if(desc->explicit_getter || desc->explicit_setter) {
398 if(desc->explicit_value)
399 hres = throw_type_error(ctx, JS_E_PROP_DESC_MISMATCH, NULL);
400 else if(desc->mask & PROPF_WRITABLE)
401 hres = throw_type_error(ctx, JS_E_INVALID_WRITABLE_PROP_DESC, NULL);
402 }
403
404 if(FAILED(hres))
405 release_property_descriptor(desc);
406 return hres;
407 }
408
409 static HRESULT Object_defineProperty(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
410 unsigned argc, jsval_t *argv, jsval_t *r)
411 {
412 property_desc_t prop_desc;
413 jsdisp_t *obj, *attr_obj;
414 const WCHAR *name;
415 jsstr_t *name_str;
416 HRESULT hres;
417
418 TRACE("\n");
419
420 if(argc < 1 || !is_object_instance(argv[0]))
421 return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
422 obj = to_jsdisp(get_object(argv[0]));
423 if(!obj) {
424 FIXME("not implemented non-JS object\n");
425 return E_NOTIMPL;
426 }
427
428 hres = to_flat_string(ctx, argc >= 2 ? argv[1] : jsval_undefined(), &name_str, &name);
429 if(FAILED(hres))
430 return hres;
431
432 if(argc >= 3 && is_object_instance(argv[2])) {
433 attr_obj = to_jsdisp(get_object(argv[2]));
434 if(attr_obj) {
435 hres = to_property_descriptor(ctx, attr_obj, &prop_desc);
436 }else {
437 FIXME("not implemented non-JS object\n");
438 hres = E_NOTIMPL;
439 }
440 }else {
441 hres = throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
442 }
443 jsstr_release(name_str);
444 if(FAILED(hres))
445 return hres;
446
447 hres = jsdisp_define_property(obj, name, &prop_desc);
448 release_property_descriptor(&prop_desc);
449 return hres;
450 }
451
452 static HRESULT Object_defineProperties(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
453 unsigned argc, jsval_t *argv, jsval_t *r)
454 {
455 FIXME("\n");
456 return E_NOTIMPL;
457 }
458
459 static HRESULT Object_getOwnPropertyDescriptor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
460 unsigned argc, jsval_t *argv, jsval_t *r)
461 {
462 property_desc_t prop_desc;
463 jsdisp_t *obj, *desc_obj;
464 const WCHAR *name;
465 jsstr_t *name_str;
466 HRESULT hres;
467
468 TRACE("\n");
469
470 if(argc < 1 || !is_object_instance(argv[0]))
471 return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
472 obj = to_jsdisp(get_object(argv[0]));
473 if(!obj) {
474 FIXME("not implemented non-JS object\n");
475 return E_NOTIMPL;
476 }
477
478 hres = to_flat_string(ctx, argc >= 2 ? argv[1] : jsval_undefined(), &name_str, &name);
479 if(FAILED(hres))
480 return hres;
481
482 hres = jsdisp_get_own_property(obj, name, FALSE, &prop_desc);
483 jsstr_release(name_str);
484 if(hres == DISP_E_UNKNOWNNAME) {
485 if(r) *r = jsval_undefined();
486 return S_OK;
487 }
488 if(FAILED(hres))
489 return hres;
490
491 hres = create_object(ctx, NULL, &desc_obj);
492 if(FAILED(hres))
493 return hres;
494
495 if(prop_desc.explicit_getter || prop_desc.explicit_setter) {
496 hres = jsdisp_define_data_property(desc_obj, getW, PROPF_ALL,
497 prop_desc.getter ? jsval_obj(prop_desc.getter) : jsval_undefined());
498 if(SUCCEEDED(hres))
499 hres = jsdisp_define_data_property(desc_obj, setW, PROPF_ALL,
500 prop_desc.setter ? jsval_obj(prop_desc.setter) : jsval_undefined());
501 }else {
502 hres = jsdisp_propput_name(desc_obj, valueW, prop_desc.value);
503 if(SUCCEEDED(hres))
504 hres = jsdisp_define_data_property(desc_obj, writableW, PROPF_ALL,
505 jsval_bool(!!(prop_desc.flags & PROPF_WRITABLE)));
506 }
507 if(SUCCEEDED(hres))
508 hres = jsdisp_define_data_property(desc_obj, enumerableW, PROPF_ALL,
509 jsval_bool(!!(prop_desc.flags & PROPF_ENUMERABLE)));
510 if(SUCCEEDED(hres))
511 hres = jsdisp_define_data_property(desc_obj, configurableW, PROPF_ALL,
512 jsval_bool(!!(prop_desc.flags & PROPF_CONFIGURABLE)));
513
514 release_property_descriptor(&prop_desc);
515 if(SUCCEEDED(hres) && r)
516 *r = jsval_obj(desc_obj);
517 else
518 jsdisp_release(desc_obj);
519 return hres;
520 }
521
522 static const builtin_prop_t ObjectConstr_props[] = {
523 {definePropertiesW, Object_defineProperties, PROPF_ES5|PROPF_METHOD|2},
524 {definePropertyW, Object_defineProperty, PROPF_ES5|PROPF_METHOD|2},
525 {getOwnPropertyDescriptorW, Object_getOwnPropertyDescriptor, PROPF_ES5|PROPF_METHOD|2}
526 };
527
528 static const builtin_info_t ObjectConstr_info = {
529 JSCLASS_FUNCTION,
530 DEFAULT_FUNCTION_VALUE,
531 ARRAY_SIZE(ObjectConstr_props),
532 ObjectConstr_props,
533 NULL,
534 NULL
535 };
536
537 static HRESULT ObjectConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
538 jsval_t *r)
539 {
540 HRESULT hres;
541
542 TRACE("\n");
543
544 switch(flags) {
545 case DISPATCH_METHOD:
546 case DISPATCH_CONSTRUCT: {
547 jsdisp_t *obj;
548
549 if(argc) {
550 if(!is_undefined(argv[0]) && !is_null(argv[0]) && (!is_object_instance(argv[0]) || get_object(argv[0]))) {
551 IDispatch *disp;
552
553 hres = to_object(ctx, argv[0], &disp);
554 if(FAILED(hres))
555 return hres;
556
557 if(r)
558 *r = jsval_disp(disp);
559 else
560 IDispatch_Release(disp);
561 return S_OK;
562 }
563 }
564
565 hres = create_object(ctx, NULL, &obj);
566 if(FAILED(hres))
567 return hres;
568
569 if(r)
570 *r = jsval_obj(obj);
571 else
572 jsdisp_release(obj);
573 break;
574 }
575
576 default:
577 FIXME("unimplemented flags: %x\n", flags);
578 return E_NOTIMPL;
579 }
580
581 return S_OK;
582 }
583
584 HRESULT create_object_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
585 {
586 static const WCHAR ObjectW[] = {'O','b','j','e','c','t',0};
587
588 return create_builtin_constructor(ctx, ObjectConstr_value, ObjectW, &ObjectConstr_info, PROPF_CONSTR,
589 object_prototype, ret);
590 }
591
592 HRESULT create_object_prototype(script_ctx_t *ctx, jsdisp_t **ret)
593 {
594 return create_dispex(ctx, &Object_info, NULL, ret);
595 }
596
597 HRESULT create_object(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t **ret)
598 {
599 jsdisp_t *object;
600 HRESULT hres;
601
602 object = heap_alloc_zero(sizeof(jsdisp_t));
603 if(!object)
604 return E_OUTOFMEMORY;
605
606 hres = init_dispex_from_constr(object, ctx, &ObjectInst_info, constr ? constr : ctx->object_constr);
607 if(FAILED(hres)) {
608 heap_free(object);
609 return hres;
610 }
611
612 *ret = object;
613 return S_OK;
614 }