2 * Copyright 2008 Jacek Caban for CodeWeavers
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
23 #include "wine/debug.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(jscript
);
27 static HRESULT
Object_toString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
33 /* Keep in sync with jsclass_t enum */
34 static const WCHAR
*names
[] = {
55 jsdisp
= get_jsdisp(jsthis
);
57 str
= L
"[object Object]";
58 }else if(names
[jsdisp
->builtin_info
->class]) {
59 str
= names
[jsdisp
->builtin_info
->class];
61 assert(jsdisp
->builtin_info
->class != JSCLASS_NONE
);
62 FIXME("jsdisp->builtin_info->class = %d\n", jsdisp
->builtin_info
->class);
68 ret
= jsstr_alloc(str
);
71 *r
= jsval_string(ret
);
77 static HRESULT
Object_toLocaleString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
82 if(!is_jsdisp(jsthis
)) {
83 FIXME("Host object this\n");
87 return jsdisp_call_name(jsthis
->u
.jsdisp
, L
"toString", DISPATCH_METHOD
, 0, NULL
, r
);
90 static HRESULT
Object_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
96 IDispatch_AddRef(jsthis
->u
.disp
);
97 *r
= jsval_disp(jsthis
->u
.disp
);
102 static HRESULT
Object_hasOwnProperty(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
114 *r
= jsval_bool(FALSE
);
118 hres
= to_string(ctx
, argv
[0], &name
);
122 if(is_jsdisp(jsthis
)) {
123 property_desc_t prop_desc
;
124 const WCHAR
*name_str
;
126 name_str
= jsstr_flatten(name
);
129 return E_OUTOFMEMORY
;
132 hres
= jsdisp_get_own_property(jsthis
->u
.jsdisp
, name_str
, TRUE
, &prop_desc
);
134 if(FAILED(hres
) && hres
!= DISP_E_UNKNOWNNAME
)
137 if(r
) *r
= jsval_bool(hres
== S_OK
);
142 bstr
= SysAllocStringLen(NULL
, jsstr_length(name
));
144 jsstr_flush(name
, bstr
);
147 return E_OUTOFMEMORY
;
149 if(is_dispex(jsthis
))
150 hres
= IDispatchEx_GetDispID(jsthis
->u
.dispex
, bstr
, make_grfdex(ctx
, fdexNameCaseSensitive
), &id
);
152 hres
= IDispatch_GetIDsOfNames(jsthis
->u
.disp
, &IID_NULL
, &bstr
, 1, ctx
->lcid
, &id
);
156 *r
= jsval_bool(SUCCEEDED(hres
));
160 static HRESULT
Object_propertyIsEnumerable(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
163 property_desc_t prop_desc
;
171 FIXME("argc %d not supported\n", argc
);
175 if(!is_jsdisp(jsthis
)) {
176 FIXME("Host object this\n");
180 hres
= to_flat_string(ctx
, argv
[0], &name_str
, &name
);
184 hres
= jsdisp_get_own_property(jsthis
->u
.jsdisp
, name
, TRUE
, &prop_desc
);
185 jsstr_release(name_str
);
186 if(FAILED(hres
) && hres
!= DISP_E_UNKNOWNNAME
)
190 *r
= jsval_bool(hres
== S_OK
&& (prop_desc
.flags
& PROPF_ENUMERABLE
) != 0);
194 static HRESULT
Object_isPrototypeOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
201 static HRESULT
Object_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
207 ret
= jsstr_alloc(L
"[object Object]");
209 return E_OUTOFMEMORY
;
211 *r
= jsval_string(ret
);
215 static void Object_destructor(jsdisp_t
*dispex
)
220 static const builtin_prop_t Object_props
[] = {
221 {L
"hasOwnProperty", Object_hasOwnProperty
, PROPF_METHOD
|1},
222 {L
"isPrototypeOf", Object_isPrototypeOf
, PROPF_METHOD
|1},
223 {L
"propertyIsEnumerable", Object_propertyIsEnumerable
, PROPF_METHOD
|1},
224 {L
"toLocaleString", Object_toLocaleString
, PROPF_METHOD
},
225 {L
"toString", Object_toString
, PROPF_METHOD
},
226 {L
"valueOf", Object_valueOf
, PROPF_METHOD
}
229 static const builtin_info_t Object_info
= {
231 {NULL
, NULL
,0, Object_get_value
},
232 ARRAY_SIZE(Object_props
),
238 static const builtin_info_t ObjectInst_info
= {
240 {NULL
, NULL
,0, Object_get_value
},
246 static void release_property_descriptor(property_desc_t
*desc
)
248 if(desc
->explicit_value
)
249 jsval_release(desc
->value
);
251 jsdisp_release(desc
->getter
);
253 jsdisp_release(desc
->setter
);
256 static HRESULT
to_property_descriptor(script_ctx_t
*ctx
, jsdisp_t
*attr_obj
, property_desc_t
*desc
)
263 memset(desc
, 0, sizeof(*desc
));
264 desc
->value
= jsval_undefined();
266 hres
= jsdisp_get_id(attr_obj
, L
"enumerable", 0, &id
);
267 if(SUCCEEDED(hres
)) {
268 desc
->mask
|= PROPF_ENUMERABLE
;
269 hres
= jsdisp_propget(attr_obj
, id
, &v
);
272 hres
= to_boolean(v
, &b
);
277 desc
->flags
|= PROPF_ENUMERABLE
;
278 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
282 hres
= jsdisp_get_id(attr_obj
, L
"configurable", 0, &id
);
283 if(SUCCEEDED(hres
)) {
284 desc
->mask
|= PROPF_CONFIGURABLE
;
285 hres
= jsdisp_propget(attr_obj
, id
, &v
);
288 hres
= to_boolean(v
, &b
);
293 desc
->flags
|= PROPF_CONFIGURABLE
;
294 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
298 hres
= jsdisp_get_id(attr_obj
, L
"value", 0, &id
);
299 if(SUCCEEDED(hres
)) {
300 hres
= jsdisp_propget(attr_obj
, id
, &desc
->value
);
303 desc
->explicit_value
= TRUE
;
304 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
308 hres
= jsdisp_get_id(attr_obj
, L
"writable", 0, &id
);
309 if(SUCCEEDED(hres
)) {
310 desc
->mask
|= PROPF_WRITABLE
;
311 hres
= jsdisp_propget(attr_obj
, id
, &v
);
312 if(SUCCEEDED(hres
)) {
313 hres
= to_boolean(v
, &b
);
315 if(SUCCEEDED(hres
) && b
)
316 desc
->flags
|= PROPF_WRITABLE
;
318 }else if(hres
== DISP_E_UNKNOWNNAME
) {
322 release_property_descriptor(desc
);
326 hres
= jsdisp_get_id(attr_obj
, L
"get", 0, &id
);
327 if(SUCCEEDED(hres
)) {
328 desc
->explicit_getter
= TRUE
;
329 hres
= jsdisp_propget(attr_obj
, id
, &v
);
330 if(SUCCEEDED(hres
) && !is_undefined(v
)) {
331 if(!is_object_instance(v
)) {
332 FIXME("getter is not an object\n");
336 /* FIXME: Check IsCallable */
337 desc
->getter
= to_jsdisp(get_object(v
));
339 FIXME("getter is not JS object\n");
342 }else if(hres
== DISP_E_UNKNOWNNAME
) {
346 release_property_descriptor(desc
);
350 hres
= jsdisp_get_id(attr_obj
, L
"set", 0, &id
);
351 if(SUCCEEDED(hres
)) {
352 desc
->explicit_setter
= TRUE
;
353 hres
= jsdisp_propget(attr_obj
, id
, &v
);
354 if(SUCCEEDED(hres
) && !is_undefined(v
)) {
355 if(!is_object_instance(v
)) {
356 FIXME("setter is not an object\n");
360 /* FIXME: Check IsCallable */
361 desc
->setter
= to_jsdisp(get_object(v
));
363 FIXME("setter is not JS object\n");
366 }else if(hres
== DISP_E_UNKNOWNNAME
) {
370 release_property_descriptor(desc
);
374 if(desc
->explicit_getter
|| desc
->explicit_setter
) {
375 if(desc
->explicit_value
)
376 hres
= JS_E_PROP_DESC_MISMATCH
;
377 else if(desc
->mask
& PROPF_WRITABLE
)
378 hres
= JS_E_INVALID_WRITABLE_PROP_DESC
;
382 release_property_descriptor(desc
);
386 static HRESULT
jsdisp_define_properties(script_ctx_t
*ctx
, jsdisp_t
*obj
, jsval_t list_val
)
388 DISPID id
= DISPID_STARTENUM
;
389 property_desc_t prop_desc
;
390 IDispatch
*list_disp
;
391 jsdisp_t
*list_obj
, *desc_obj
;
396 hres
= to_object(ctx
, list_val
, &list_disp
);
400 if(!(list_obj
= to_jsdisp(list_disp
))) {
401 FIXME("non-JS list obj\n");
402 IDispatch_Release(list_disp
);
407 hres
= jsdisp_next_prop(list_obj
, id
, TRUE
, &id
);
411 hres
= jsdisp_propget(list_obj
, id
, &desc_val
);
415 if(!is_object_instance(desc_val
) || !get_object(desc_val
) || !(desc_obj
= to_jsdisp(get_object(desc_val
)))) {
416 jsval_release(desc_val
);
420 hres
= to_property_descriptor(ctx
, desc_obj
, &prop_desc
);
421 jsdisp_release(desc_obj
);
425 hres
= IDispatchEx_GetMemberName(&list_obj
->IDispatchEx_iface
, id
, &name
);
427 hres
= jsdisp_define_property(obj
, name
, &prop_desc
);
428 release_property_descriptor(&prop_desc
);
433 jsdisp_release(list_obj
);
434 return FAILED(hres
) ? hres
: S_OK
;
437 static HRESULT
Object_defineProperty(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
438 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
440 property_desc_t prop_desc
;
441 jsdisp_t
*obj
, *attr_obj
;
448 if(argc
< 1 || !is_object_instance(argv
[0]))
449 return JS_E_OBJECT_EXPECTED
;
450 obj
= to_jsdisp(get_object(argv
[0]));
452 FIXME("not implemented non-JS object\n");
456 hres
= to_flat_string(ctx
, argc
>= 2 ? argv
[1] : jsval_undefined(), &name_str
, &name
);
460 if(argc
>= 3 && is_object_instance(argv
[2])) {
461 attr_obj
= to_jsdisp(get_object(argv
[2]));
463 hres
= to_property_descriptor(ctx
, attr_obj
, &prop_desc
);
465 FIXME("not implemented non-JS object\n");
469 hres
= JS_E_OBJECT_EXPECTED
;
471 jsstr_release(name_str
);
475 hres
= jsdisp_define_property(obj
, name
, &prop_desc
);
476 release_property_descriptor(&prop_desc
);
477 if(SUCCEEDED(hres
) && r
)
478 *r
= jsval_obj(jsdisp_addref(obj
));
482 static HRESULT
Object_defineProperties(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
483 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
488 if(argc
< 1 || !is_object_instance(argv
[0]) || !get_object(argv
[0]) || !(obj
= to_jsdisp(get_object(argv
[0])))) {
489 FIXME("not an object\n");
495 hres
= jsdisp_define_properties(ctx
, obj
, argc
>= 2 ? argv
[1] : jsval_undefined());
496 if(SUCCEEDED(hres
) && r
)
497 *r
= jsval_obj(jsdisp_addref(obj
));
501 static HRESULT
Object_getOwnPropertyDescriptor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
502 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
504 property_desc_t prop_desc
;
505 jsdisp_t
*obj
, *desc_obj
;
512 if(argc
< 1 || !is_object_instance(argv
[0]))
513 return JS_E_OBJECT_EXPECTED
;
514 obj
= to_jsdisp(get_object(argv
[0]));
516 FIXME("not implemented non-JS object\n");
520 hres
= to_flat_string(ctx
, argc
>= 2 ? argv
[1] : jsval_undefined(), &name_str
, &name
);
524 hres
= jsdisp_get_own_property(obj
, name
, FALSE
, &prop_desc
);
525 jsstr_release(name_str
);
526 if(hres
== DISP_E_UNKNOWNNAME
) {
527 if(r
) *r
= jsval_undefined();
533 hres
= create_object(ctx
, NULL
, &desc_obj
);
537 if(prop_desc
.explicit_getter
|| prop_desc
.explicit_setter
) {
538 hres
= jsdisp_define_data_property(desc_obj
, L
"get", PROPF_ALL
,
539 prop_desc
.getter
? jsval_obj(prop_desc
.getter
) : jsval_undefined());
541 hres
= jsdisp_define_data_property(desc_obj
, L
"set", PROPF_ALL
,
542 prop_desc
.setter
? jsval_obj(prop_desc
.setter
) : jsval_undefined());
544 hres
= jsdisp_propput_name(desc_obj
, L
"value", prop_desc
.value
);
546 hres
= jsdisp_define_data_property(desc_obj
, L
"writable", PROPF_ALL
,
547 jsval_bool(!!(prop_desc
.flags
& PROPF_WRITABLE
)));
550 hres
= jsdisp_define_data_property(desc_obj
, L
"enumerable", PROPF_ALL
,
551 jsval_bool(!!(prop_desc
.flags
& PROPF_ENUMERABLE
)));
553 hres
= jsdisp_define_data_property(desc_obj
, L
"configurable", PROPF_ALL
,
554 jsval_bool(!!(prop_desc
.flags
& PROPF_CONFIGURABLE
)));
556 release_property_descriptor(&prop_desc
);
557 if(SUCCEEDED(hres
) && r
)
558 *r
= jsval_obj(desc_obj
);
560 jsdisp_release(desc_obj
);
564 static HRESULT
Object_create(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
565 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
567 jsdisp_t
*proto
= NULL
, *obj
;
570 if(!argc
|| (!is_object_instance(argv
[0]) && !is_null(argv
[0]))) {
571 FIXME("Invalid arg\n");
575 TRACE("(%s)\n", debugstr_jsval(argv
[0]));
577 if(argc
&& is_object_instance(argv
[0])) {
578 if(get_object(argv
[0]))
579 proto
= to_jsdisp(get_object(argv
[0]));
581 FIXME("Non-JS prototype\n");
584 }else if(!is_null(argv
[0])) {
585 FIXME("Invalid arg %s\n", debugstr_jsval(argc
? argv
[0] : jsval_undefined()));
589 hres
= create_dispex(ctx
, &ObjectInst_info
, proto
, &obj
);
593 if(argc
>= 2 && !is_undefined(argv
[1]))
594 hres
= jsdisp_define_properties(ctx
, obj
, argv
[1]);
596 if(SUCCEEDED(hres
) && r
)
603 static HRESULT
Object_getPrototypeOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
604 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
608 if(!argc
|| !is_object_instance(argv
[0])) {
609 FIXME("invalid arguments\n");
613 TRACE("(%s)\n", debugstr_jsval(argv
[0]));
615 obj
= to_jsdisp(get_object(argv
[0]));
617 FIXME("Non-JS object\n");
623 ? jsval_obj(jsdisp_addref(obj
->prototype
))
628 static HRESULT
Object_keys(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
629 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
631 DISPID id
= DISPID_STARTENUM
;
632 jsdisp_t
*obj
, *array
;
637 if(!argc
|| !is_object_instance(argv
[0])) {
638 FIXME("invalid arguments %s\n", debugstr_jsval(argv
[0]));
642 TRACE("(%s)\n", debugstr_jsval(argv
[0]));
644 obj
= to_jsdisp(get_object(argv
[0]));
646 FIXME("Non-JS object\n");
650 hres
= create_array(ctx
, 0, &array
);
655 hres
= jsdisp_next_prop(obj
, id
, TRUE
, &id
);
659 hres
= jsdisp_get_prop_name(obj
, id
, &key
);
663 hres
= jsdisp_propput_idx(array
, i
++, jsval_string(key
));
665 } while(hres
== S_OK
);
667 if(SUCCEEDED(hres
) && r
)
668 *r
= jsval_obj(array
);
670 jsdisp_release(array
);
674 static HRESULT
Object_preventExtensions(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
678 if(!argc
|| !is_object_instance(argv
[0]) || !get_object(argv
[0])) {
679 FIXME("invalid arguments\n");
683 FIXME("(%s) semi-stub\n", debugstr_jsval(argv
[0]));
685 obj
= to_jsdisp(get_object(argv
[0]));
687 FIXME("Non-JS object\n");
691 if(r
) *r
= jsval_obj(jsdisp_addref(obj
));
695 static const builtin_prop_t ObjectConstr_props
[] = {
696 {L
"create", Object_create
, PROPF_ES5
|PROPF_METHOD
|2},
697 {L
"defineProperties", Object_defineProperties
, PROPF_ES5
|PROPF_METHOD
|2},
698 {L
"defineProperty", Object_defineProperty
, PROPF_ES5
|PROPF_METHOD
|2},
699 {L
"getOwnPropertyDescriptor", Object_getOwnPropertyDescriptor
, PROPF_ES5
|PROPF_METHOD
|2},
700 {L
"getPrototypeOf", Object_getPrototypeOf
, PROPF_ES5
|PROPF_METHOD
|1},
701 {L
"keys", Object_keys
, PROPF_ES5
|PROPF_METHOD
|1},
702 {L
"preventExtensions", Object_preventExtensions
, PROPF_ES5
|PROPF_METHOD
|1},
705 static const builtin_info_t ObjectConstr_info
= {
707 DEFAULT_FUNCTION_VALUE
,
708 ARRAY_SIZE(ObjectConstr_props
),
714 static HRESULT
ObjectConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
722 case DISPATCH_METHOD
:
723 case DISPATCH_CONSTRUCT
: {
727 if(!is_undefined(argv
[0]) && !is_null(argv
[0]) && (!is_object_instance(argv
[0]) || get_object(argv
[0]))) {
730 hres
= to_object(ctx
, argv
[0], &disp
);
735 *r
= jsval_disp(disp
);
737 IDispatch_Release(disp
);
742 hres
= create_object(ctx
, NULL
, &obj
);
754 FIXME("unimplemented flags: %x\n", flags
);
761 HRESULT
create_object_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
763 return create_builtin_constructor(ctx
, ObjectConstr_value
, L
"Object", &ObjectConstr_info
, PROPF_CONSTR
,
764 object_prototype
, ret
);
767 HRESULT
create_object_prototype(script_ctx_t
*ctx
, jsdisp_t
**ret
)
769 return create_dispex(ctx
, &Object_info
, NULL
, ret
);
772 HRESULT
create_object(script_ctx_t
*ctx
, jsdisp_t
*constr
, jsdisp_t
**ret
)
777 object
= heap_alloc_zero(sizeof(jsdisp_t
));
779 return E_OUTOFMEMORY
;
781 hres
= init_dispex_from_constr(object
, ctx
, &ObjectInst_info
, constr
? constr
: ctx
->object_constr
);