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, objectW, 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 } 615