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 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};
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};
39 static const WCHAR definePropertiesW
[] = {'d','e','f','i','n','e','P','r','o','p','e','r','t','i','e','s',0};
41 static const WCHAR default_valueW
[] = {'[','o','b','j','e','c','t',' ','O','b','j','e','c','t',']',0};
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};
50 static HRESULT
Object_toString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
56 static const WCHAR formatW
[] = {'[','o','b','j','e','c','t',' ','%','s',']',0};
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
};
74 jsdisp
= get_jsdisp(jsthis
);
77 }else if(names
[jsdisp
->builtin_info
->class]) {
78 str
= names
[jsdisp
->builtin_info
->class];
80 assert(jsdisp
->builtin_info
->class != JSCLASS_NONE
);
81 FIXME("jdisp->builtin_info->class = %d\n", jsdisp
->builtin_info
->class);
89 ret
= jsstr_alloc_buf(9+strlenW(str
), &ptr
);
93 sprintfW(ptr
, formatW
, str
);
94 *r
= jsval_string(ret
);
100 static HRESULT
Object_toLocaleString(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
105 if(!is_jsdisp(jsthis
)) {
106 FIXME("Host object this\n");
110 return jsdisp_call_name(jsthis
->u
.jsdisp
, toStringW
, DISPATCH_METHOD
, 0, NULL
, r
);
113 static HRESULT
Object_valueOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
119 IDispatch_AddRef(jsthis
->u
.disp
);
120 *r
= jsval_disp(jsthis
->u
.disp
);
125 static HRESULT
Object_hasOwnProperty(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
137 *r
= jsval_bool(FALSE
);
141 hres
= to_string(ctx
, argv
[0], &name
);
145 if(is_jsdisp(jsthis
)) {
146 property_desc_t prop_desc
;
147 const WCHAR
*name_str
;
149 name_str
= jsstr_flatten(name
);
152 return E_OUTOFMEMORY
;
155 hres
= jsdisp_get_own_property(jsthis
->u
.jsdisp
, name_str
, TRUE
, &prop_desc
);
157 if(FAILED(hres
) && hres
!= DISP_E_UNKNOWNNAME
)
160 if(r
) *r
= jsval_bool(hres
== S_OK
);
165 bstr
= SysAllocStringLen(NULL
, jsstr_length(name
));
167 jsstr_flush(name
, bstr
);
170 return E_OUTOFMEMORY
;
172 if(is_dispex(jsthis
))
173 hres
= IDispatchEx_GetDispID(jsthis
->u
.dispex
, bstr
, make_grfdex(ctx
, fdexNameCaseSensitive
), &id
);
175 hres
= IDispatch_GetIDsOfNames(jsthis
->u
.disp
, &IID_NULL
, &bstr
, 1, ctx
->lcid
, &id
);
179 *r
= jsval_bool(SUCCEEDED(hres
));
183 static HRESULT
Object_propertyIsEnumerable(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
186 property_desc_t prop_desc
;
194 FIXME("argc %d not supported\n", argc
);
198 if(!is_jsdisp(jsthis
)) {
199 FIXME("Host object this\n");
203 hres
= to_flat_string(ctx
, argv
[0], &name_str
, &name
);
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
)
213 *r
= jsval_bool(hres
== S_OK
&& (prop_desc
.flags
& PROPF_ENUMERABLE
) != 0);
217 static HRESULT
Object_isPrototypeOf(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
224 static HRESULT
Object_get_value(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t
*r
)
230 ret
= jsstr_alloc(default_valueW
);
232 return E_OUTOFMEMORY
;
234 *r
= jsval_string(ret
);
238 static void Object_destructor(jsdisp_t
*dispex
)
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
}
252 static const builtin_info_t Object_info
= {
254 {NULL
, NULL
,0, Object_get_value
},
255 sizeof(Object_props
)/sizeof(*Object_props
),
261 static const builtin_info_t ObjectInst_info
= {
263 {NULL
, NULL
,0, Object_get_value
},
269 static void release_property_descriptor(property_desc_t
*desc
)
271 if(desc
->explicit_value
)
272 jsval_release(desc
->value
);
274 jsdisp_release(desc
->getter
);
276 jsdisp_release(desc
->setter
);
279 static HRESULT
to_property_descriptor(script_ctx_t
*ctx
, jsdisp_t
*attr_obj
, property_desc_t
*desc
)
286 memset(desc
, 0, sizeof(*desc
));
287 desc
->value
= jsval_undefined();
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
);
295 hres
= to_boolean(v
, &b
);
300 desc
->flags
|= PROPF_ENUMERABLE
;
301 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
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
);
311 hres
= to_boolean(v
, &b
);
316 desc
->flags
|= PROPF_CONFIGURABLE
;
317 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
321 hres
= jsdisp_get_id(attr_obj
, valueW
, 0, &id
);
322 if(SUCCEEDED(hres
)) {
323 hres
= jsdisp_propget(attr_obj
, id
, &desc
->value
);
326 desc
->explicit_value
= TRUE
;
327 }else if(hres
!= DISP_E_UNKNOWNNAME
) {
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
);
338 if(SUCCEEDED(hres
) && b
)
339 desc
->flags
|= PROPF_WRITABLE
;
341 }else if(hres
== DISP_E_UNKNOWNNAME
) {
345 release_property_descriptor(desc
);
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");
359 /* FIXME: Check IsCallable */
360 desc
->getter
= to_jsdisp(get_object(v
));
362 FIXME("getter is not JS object\n");
365 }else if(hres
== DISP_E_UNKNOWNNAME
) {
369 release_property_descriptor(desc
);
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");
383 /* FIXME: Check IsCallable */
384 desc
->setter
= to_jsdisp(get_object(v
));
386 FIXME("setter is not JS object\n");
389 }else if(hres
== DISP_E_UNKNOWNNAME
) {
393 release_property_descriptor(desc
);
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
);
405 release_property_descriptor(desc
);
409 static HRESULT
Object_defineProperty(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
410 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
412 property_desc_t prop_desc
;
413 jsdisp_t
*obj
, *attr_obj
;
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]));
424 FIXME("not implemented non-JS object\n");
428 hres
= to_flat_string(ctx
, argc
>= 2 ? argv
[1] : jsval_undefined(), &name_str
, &name
);
432 if(argc
>= 3 && is_object_instance(argv
[2])) {
433 attr_obj
= to_jsdisp(get_object(argv
[2]));
435 hres
= to_property_descriptor(ctx
, attr_obj
, &prop_desc
);
437 FIXME("not implemented non-JS object\n");
441 hres
= throw_type_error(ctx
, JS_E_OBJECT_EXPECTED
, NULL
);
443 jsstr_release(name_str
);
447 hres
= jsdisp_define_property(obj
, name
, &prop_desc
);
448 release_property_descriptor(&prop_desc
);
452 static HRESULT
Object_defineProperties(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
453 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
459 static HRESULT
Object_getOwnPropertyDescriptor(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
,
460 unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
462 property_desc_t prop_desc
;
463 jsdisp_t
*obj
, *desc_obj
;
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]));
474 FIXME("not implemented non-JS object\n");
478 hres
= to_flat_string(ctx
, argc
>= 2 ? argv
[1] : jsval_undefined(), &name_str
, &name
);
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();
491 hres
= create_object(ctx
, NULL
, &desc_obj
);
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());
499 hres
= jsdisp_define_data_property(desc_obj
, setW
, PROPF_ALL
,
500 prop_desc
.setter
? jsval_obj(prop_desc
.setter
) : jsval_undefined());
502 hres
= jsdisp_propput_name(desc_obj
, valueW
, prop_desc
.value
);
504 hres
= jsdisp_define_data_property(desc_obj
, writableW
, PROPF_ALL
,
505 jsval_bool(!!(prop_desc
.flags
& PROPF_WRITABLE
)));
508 hres
= jsdisp_define_data_property(desc_obj
, enumerableW
, PROPF_ALL
,
509 jsval_bool(!!(prop_desc
.flags
& PROPF_ENUMERABLE
)));
511 hres
= jsdisp_define_data_property(desc_obj
, configurableW
, PROPF_ALL
,
512 jsval_bool(!!(prop_desc
.flags
& PROPF_CONFIGURABLE
)));
514 release_property_descriptor(&prop_desc
);
515 if(SUCCEEDED(hres
) && r
)
516 *r
= jsval_obj(desc_obj
);
518 jsdisp_release(desc_obj
);
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}
528 static const builtin_info_t ObjectConstr_info
= {
530 DEFAULT_FUNCTION_VALUE
,
531 sizeof(ObjectConstr_props
)/sizeof(*ObjectConstr_props
),
537 static HRESULT
ObjectConstr_value(script_ctx_t
*ctx
, vdisp_t
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
545 case DISPATCH_METHOD
:
546 case DISPATCH_CONSTRUCT
: {
550 if(!is_undefined(argv
[0]) && !is_null(argv
[0]) && (!is_object_instance(argv
[0]) || get_object(argv
[0]))) {
553 hres
= to_object(ctx
, argv
[0], &disp
);
558 *r
= jsval_disp(disp
);
560 IDispatch_Release(disp
);
565 hres
= create_object(ctx
, NULL
, &obj
);
577 FIXME("unimplemented flags: %x\n", flags
);
584 HRESULT
create_object_constr(script_ctx_t
*ctx
, jsdisp_t
*object_prototype
, jsdisp_t
**ret
)
586 static const WCHAR ObjectW
[] = {'O','b','j','e','c','t',0};
588 return create_builtin_constructor(ctx
, ObjectConstr_value
, ObjectW
, &ObjectConstr_info
, PROPF_CONSTR
,
589 object_prototype
, ret
);
592 HRESULT
create_object_prototype(script_ctx_t
*ctx
, jsdisp_t
**ret
)
594 return create_dispex(ctx
, &Object_info
, NULL
, ret
);
597 HRESULT
create_object(script_ctx_t
*ctx
, jsdisp_t
*constr
, jsdisp_t
**ret
)
602 object
= heap_alloc_zero(sizeof(jsdisp_t
));
604 return E_OUTOFMEMORY
;
606 hres
= init_dispex_from_constr(object
, ctx
, &ObjectInst_info
, constr
? constr
: ctx
->object_constr
);