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