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 #include "engine.h" 23 24 #include "wine/debug.h" 25 26 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 27 28 typedef struct _function_vtbl_t function_vtbl_t; 29 30 typedef struct { 31 jsdisp_t dispex; 32 const function_vtbl_t *vtbl; 33 DWORD flags; 34 DWORD length; 35 } FunctionInstance; 36 37 struct _function_vtbl_t { 38 HRESULT (*call)(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,unsigned,jsval_t*,jsval_t*); 39 HRESULT (*toString)(FunctionInstance*,jsstr_t**); 40 void (*destructor)(FunctionInstance*); 41 }; 42 43 typedef struct { 44 FunctionInstance function; 45 scope_chain_t *scope_chain; 46 bytecode_t *code; 47 function_code_t *func_code; 48 } InterpretedFunction; 49 50 typedef struct { 51 FunctionInstance function; 52 builtin_invoke_t proc; 53 const WCHAR *name; 54 } NativeFunction; 55 56 typedef struct { 57 FunctionInstance function; 58 FunctionInstance *target; 59 IDispatch *this; 60 unsigned argc; 61 jsval_t args[1]; 62 } BindFunction; 63 64 typedef struct { 65 jsdisp_t jsdisp; 66 InterpretedFunction *function; 67 jsval_t *buf; 68 call_frame_t *frame; 69 unsigned argc; 70 } ArgumentsInstance; 71 72 static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,jsval_t*,jsdisp_t**r); 73 74 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp) 75 { 76 return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex); 77 } 78 79 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp) 80 { 81 return function_from_jsdisp(vdisp->u.jsdisp); 82 } 83 84 static inline FunctionInstance *function_this(vdisp_t *jsthis) 85 { 86 return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL; 87 } 88 89 static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp) 90 { 91 return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp); 92 } 93 94 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0}; 95 96 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; 97 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; 98 static const WCHAR applyW[] = {'a','p','p','l','y',0}; 99 static const WCHAR bindW[] = {'b','i','n','d',0}; 100 static const WCHAR callW[] = {'c','a','l','l',0}; 101 static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0}; 102 103 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 104 jsval_t *r) 105 { 106 FIXME("\n"); 107 return E_NOTIMPL; 108 } 109 110 static void Arguments_destructor(jsdisp_t *jsdisp) 111 { 112 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); 113 114 TRACE("(%p)\n", arguments); 115 116 if(arguments->buf) { 117 unsigned i; 118 for(i = 0; i < arguments->argc; i++) 119 jsval_release(arguments->buf[i]); 120 heap_free(arguments->buf); 121 } 122 123 jsdisp_release(&arguments->function->function.dispex); 124 heap_free(arguments); 125 } 126 127 static unsigned Arguments_idx_length(jsdisp_t *jsdisp) 128 { 129 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); 130 return arguments->argc; 131 } 132 133 static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx) 134 { 135 if(arguments->buf) 136 return arguments->buf + idx; 137 if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt) 138 return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx; 139 return NULL; 140 } 141 142 static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) 143 { 144 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); 145 jsval_t *ref; 146 147 TRACE("%p[%u]\n", arguments, idx); 148 149 if((ref = get_argument_ref(arguments, idx))) 150 return jsval_copy(*ref, r); 151 152 /* FIXME: Accessing by name won't work for duplicated argument names */ 153 return jsdisp_propget_name(arguments->frame->base_scope->jsobj, 154 arguments->function->func_code->params[idx], r); 155 } 156 157 static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val) 158 { 159 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); 160 jsval_t *ref; 161 HRESULT hres; 162 163 TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val)); 164 165 if((ref = get_argument_ref(arguments, idx))) { 166 jsval_t copy; 167 hres = jsval_copy(val, ©); 168 if(FAILED(hres)) 169 return hres; 170 171 jsval_release(*ref); 172 *ref = copy; 173 return S_OK; 174 } 175 176 /* FIXME: Accessing by name won't work for duplicated argument names */ 177 return jsdisp_propput_name(arguments->frame->base_scope->jsobj, 178 arguments->function->func_code->params[idx], val); 179 } 180 181 static const builtin_info_t Arguments_info = { 182 JSCLASS_ARGUMENTS, 183 {NULL, Arguments_value, 0}, 184 0, NULL, 185 Arguments_destructor, 186 NULL, 187 Arguments_idx_length, 188 Arguments_idx_get, 189 Arguments_idx_put 190 }; 191 192 HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) 193 { 194 ArgumentsInstance *args; 195 HRESULT hres; 196 197 static const WCHAR caleeW[] = {'c','a','l','l','e','e',0}; 198 199 args = heap_alloc_zero(sizeof(*args)); 200 if(!args) 201 return E_OUTOFMEMORY; 202 203 hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr); 204 if(FAILED(hres)) { 205 heap_free(args); 206 return hres; 207 } 208 209 args->function = (InterpretedFunction*)function_from_jsdisp(jsdisp_addref(frame->function_instance)); 210 args->argc = frame->argc; 211 args->frame = frame; 212 213 hres = jsdisp_define_data_property(&args->jsdisp, lengthW, PROPF_WRITABLE | PROPF_CONFIGURABLE, 214 jsval_number(args->argc)); 215 if(SUCCEEDED(hres)) 216 hres = jsdisp_define_data_property(&args->jsdisp, caleeW, PROPF_WRITABLE | PROPF_CONFIGURABLE, 217 jsval_obj(&args->function->function.dispex)); 218 if(SUCCEEDED(hres)) 219 hres = jsdisp_propput(frame->base_scope->jsobj, argumentsW, PROPF_WRITABLE, jsval_obj(&args->jsdisp)); 220 if(FAILED(hres)) { 221 jsdisp_release(&args->jsdisp); 222 return hres; 223 } 224 225 frame->arguments_obj = &args->jsdisp; 226 return S_OK; 227 } 228 229 void detach_arguments_object(jsdisp_t *args_disp) 230 { 231 ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp); 232 call_frame_t *frame = arguments->frame; 233 const BOOL on_stack = frame->base_scope->frame == frame; 234 HRESULT hres; 235 236 /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have 237 * their own arguments property, it's impossible to use prototype's one during name lookup */ 238 jsdisp_propput_name(frame->base_scope->jsobj, argumentsW, jsval_undefined()); 239 arguments->frame = NULL; 240 241 /* Don't bother coppying arguments if call frame holds the last reference. */ 242 if(arguments->jsdisp.ref > 1) { 243 arguments->buf = heap_alloc(arguments->argc * sizeof(*arguments->buf)); 244 if(arguments->buf) { 245 int i; 246 247 for(i = 0; i < arguments->argc ; i++) { 248 if(on_stack || i >= frame->function->param_cnt) 249 hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i); 250 else 251 hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i); 252 if(FAILED(hres)) 253 arguments->buf[i] = jsval_undefined(); 254 } 255 }else { 256 ERR("out of memory\n"); 257 arguments->argc = 0; 258 } 259 } 260 261 jsdisp_release(frame->arguments_obj); 262 } 263 264 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) 265 { 266 FunctionInstance *function; 267 268 TRACE("func %p this %p\n", func_this, jsthis); 269 270 assert(is_class(func_this, JSCLASS_FUNCTION)); 271 function = function_from_jsdisp(func_this); 272 273 return function->vtbl->call(function->dispex.ctx, function, jsthis, flags, argc, argv, r); 274 } 275 276 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 277 { 278 TRACE("%p\n", jsthis); 279 280 *r = jsval_number(function_from_jsdisp(jsthis)->length); 281 return S_OK; 282 } 283 284 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 285 jsval_t *r) 286 { 287 FunctionInstance *function; 288 jsstr_t *str; 289 HRESULT hres; 290 291 TRACE("\n"); 292 293 if(!(function = function_this(jsthis))) 294 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 295 296 hres = function->vtbl->toString(function, &str); 297 if(FAILED(hres)) 298 return hres; 299 300 if(r) 301 *r = jsval_string(str); 302 else 303 jsstr_release(str); 304 return S_OK; 305 } 306 307 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret) 308 { 309 jsval_t *argv, val; 310 DWORD length, i; 311 HRESULT hres; 312 313 hres = jsdisp_propget_name(arg_array, lengthW, &val); 314 if(FAILED(hres)) 315 return hres; 316 317 hres = to_uint32(ctx, val, &length); 318 jsval_release(val); 319 if(FAILED(hres)) 320 return hres; 321 322 argv = heap_alloc(length * sizeof(*argv)); 323 if(!argv) 324 return E_OUTOFMEMORY; 325 326 for(i=0; i<length; i++) { 327 hres = jsdisp_get_idx(arg_array, i, argv+i); 328 if(hres == DISP_E_UNKNOWNNAME) { 329 argv[i] = jsval_undefined(); 330 }else if(FAILED(hres)) { 331 while(i--) 332 jsval_release(argv[i]); 333 heap_free(argv); 334 return hres; 335 } 336 } 337 338 *argc = length; 339 *ret = argv; 340 return S_OK; 341 } 342 343 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) 344 { 345 FunctionInstance *function; 346 jsval_t *args = NULL; 347 unsigned i, cnt = 0; 348 IDispatch *this_obj = NULL; 349 HRESULT hres = S_OK; 350 351 TRACE("\n"); 352 353 if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP)) 354 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 355 356 if(argc) { 357 if(!is_undefined(argv[0]) && !is_null(argv[0])) { 358 hres = to_object(ctx, argv[0], &this_obj); 359 if(FAILED(hres)) 360 return hres; 361 } 362 } 363 364 if(argc >= 2) { 365 jsdisp_t *arg_array = NULL; 366 367 if(is_object_instance(argv[1])) { 368 arg_array = iface_to_jsdisp(get_object(argv[1])); 369 if(arg_array && 370 (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) { 371 jsdisp_release(arg_array); 372 arg_array = NULL; 373 } 374 } 375 376 if(arg_array) { 377 hres = array_to_args(ctx, arg_array, &cnt, &args); 378 jsdisp_release(arg_array); 379 }else { 380 FIXME("throw TypeError\n"); 381 hres = E_FAIL; 382 } 383 } 384 385 if(SUCCEEDED(hres)) { 386 if(function) { 387 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, args, r); 388 }else { 389 jsval_t res; 390 hres = disp_call_value(ctx, jsthis->u.disp, this_obj, DISPATCH_METHOD, cnt, args, &res); 391 if(SUCCEEDED(hres)) { 392 if(r) 393 *r = res; 394 else 395 jsval_release(res); 396 } 397 } 398 } 399 400 if(this_obj) 401 IDispatch_Release(this_obj); 402 for(i=0; i < cnt; i++) 403 jsval_release(args[i]); 404 heap_free(args); 405 return hres; 406 } 407 408 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 409 jsval_t *r) 410 { 411 FunctionInstance *function; 412 IDispatch *this_obj = NULL; 413 unsigned cnt = 0; 414 HRESULT hres; 415 416 TRACE("\n"); 417 418 if(!(function = function_this(jsthis))) 419 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 420 421 if(argc) { 422 if(!is_undefined(argv[0]) && !is_null(argv[0])) { 423 hres = to_object(ctx, argv[0], &this_obj); 424 if(FAILED(hres)) 425 return hres; 426 } 427 428 cnt = argc-1; 429 } 430 431 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, argv + 1, r); 432 433 if(this_obj) 434 IDispatch_Release(this_obj); 435 return hres; 436 } 437 438 static HRESULT Function_bind(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 439 jsval_t *r) 440 { 441 FunctionInstance *function; 442 jsdisp_t *new_function; 443 HRESULT hres; 444 445 TRACE("\n"); 446 447 if(!(function = function_this(jsthis))) 448 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 449 450 if(argc < 1) { 451 FIXME("no this argument\n"); 452 return E_NOTIMPL; 453 } 454 455 if(!is_object_instance(argv[0]) || !get_object(argv[0])) { 456 FIXME("%s is not an object instance\n", debugstr_jsval(argv[0])); 457 return E_NOTIMPL; 458 } 459 460 hres = create_bind_function(ctx, function, get_object(argv[0]), argc - 1, argv + 1, &new_function); 461 if(FAILED(hres)) 462 return hres; 463 464 if(r) 465 *r = jsval_obj(new_function); 466 else 467 jsdisp_release(new_function); 468 return S_OK; 469 } 470 471 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 472 jsval_t *r) 473 { 474 FunctionInstance *function; 475 476 TRACE("\n"); 477 478 if(!is_vclass(jsthis, JSCLASS_FUNCTION)) { 479 ERR("dispex is not a function\n"); 480 return E_FAIL; 481 } 482 483 function = function_from_jsdisp(jsthis->u.jsdisp); 484 return function->vtbl->call(ctx, function, NULL, flags, argc, argv, r); 485 } 486 487 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 488 { 489 FunctionInstance *function = function_from_jsdisp(jsthis); 490 jsstr_t *str; 491 HRESULT hres; 492 493 TRACE("\n"); 494 495 hres = function->vtbl->toString(function, &str); 496 if(FAILED(hres)) 497 return hres; 498 499 *r = jsval_string(str); 500 return S_OK; 501 } 502 503 static HRESULT Function_get_arguments(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 504 { 505 FunctionInstance *function = function_from_jsdisp(jsthis); 506 call_frame_t *frame; 507 HRESULT hres; 508 509 TRACE("\n"); 510 511 for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) { 512 if(frame->function_instance == &function->dispex) { 513 if(!frame->arguments_obj) { 514 hres = setup_arguments_object(ctx, frame); 515 if(FAILED(hres)) 516 return hres; 517 } 518 *r = jsval_obj(jsdisp_addref(frame->arguments_obj)); 519 return S_OK; 520 } 521 } 522 523 *r = jsval_null(); 524 return S_OK; 525 } 526 527 static void Function_destructor(jsdisp_t *dispex) 528 { 529 FunctionInstance *function = function_from_jsdisp(dispex); 530 function->vtbl->destructor(function); 531 heap_free(function); 532 } 533 534 static const builtin_prop_t Function_props[] = { 535 {applyW, Function_apply, PROPF_METHOD|2}, 536 {argumentsW, NULL, 0, Function_get_arguments}, 537 {bindW, Function_bind, PROPF_METHOD|PROPF_ES5|1}, 538 {callW, Function_call, PROPF_METHOD|1}, 539 {lengthW, NULL, 0, Function_get_length}, 540 {toStringW, Function_toString, PROPF_METHOD} 541 }; 542 543 static const builtin_info_t Function_info = { 544 JSCLASS_FUNCTION, 545 DEFAULT_FUNCTION_VALUE, 546 ARRAY_SIZE(Function_props), 547 Function_props, 548 Function_destructor, 549 NULL 550 }; 551 552 static const builtin_prop_t FunctionInst_props[] = { 553 {argumentsW, NULL, 0, Function_get_arguments}, 554 {lengthW, NULL, 0, Function_get_length} 555 }; 556 557 static const builtin_info_t FunctionInst_info = { 558 JSCLASS_FUNCTION, 559 DEFAULT_FUNCTION_VALUE, 560 ARRAY_SIZE(FunctionInst_props), 561 FunctionInst_props, 562 Function_destructor, 563 NULL 564 }; 565 566 static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, const function_vtbl_t *vtbl, size_t size, 567 DWORD flags, BOOL funcprot, jsdisp_t *prototype, void **ret) 568 { 569 FunctionInstance *function; 570 HRESULT hres; 571 572 function = heap_alloc_zero(size); 573 if(!function) 574 return E_OUTOFMEMORY; 575 576 if(funcprot) 577 hres = init_dispex(&function->dispex, ctx, builtin_info, prototype); 578 else if(builtin_info) 579 hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr); 580 else 581 hres = init_dispex_from_constr(&function->dispex, ctx, &FunctionInst_info, ctx->function_constr); 582 if(FAILED(hres)) { 583 heap_free(function); 584 return hres; 585 } 586 587 function->vtbl = vtbl; 588 function->flags = flags; 589 function->length = flags & PROPF_ARGMASK; 590 591 *ret = function; 592 return S_OK; 593 } 594 595 static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_disp, unsigned flags, 596 unsigned argc, jsval_t *argv, jsval_t *r) 597 { 598 NativeFunction *function = (NativeFunction*)func; 599 vdisp_t vthis; 600 HRESULT hres; 601 602 if(this_disp) 603 set_disp(&vthis, this_disp); 604 else if(ctx->host_global) 605 set_disp(&vthis, ctx->host_global); 606 else 607 set_jsdisp(&vthis, ctx->global); 608 609 hres = function->proc(ctx, &vthis, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r); 610 611 vdisp_release(&vthis); 612 return hres; 613 } 614 615 static HRESULT NativeFunction_toString(FunctionInstance *func, jsstr_t **ret) 616 { 617 NativeFunction *function = (NativeFunction*)func; 618 DWORD name_len; 619 jsstr_t *str; 620 WCHAR *ptr; 621 622 static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '}; 623 static const WCHAR native_suffixW[] = 624 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'}; 625 626 name_len = function->name ? lstrlenW(function->name) : 0; 627 str = jsstr_alloc_buf(ARRAY_SIZE(native_prefixW) + ARRAY_SIZE(native_suffixW) + name_len, &ptr); 628 if(!str) 629 return E_OUTOFMEMORY; 630 631 memcpy(ptr, native_prefixW, sizeof(native_prefixW)); 632 ptr += ARRAY_SIZE(native_prefixW); 633 memcpy(ptr, function->name, name_len*sizeof(WCHAR)); 634 ptr += name_len; 635 memcpy(ptr, native_suffixW, sizeof(native_suffixW)); 636 637 *ret = str; 638 return S_OK; 639 } 640 641 static void NativeFunction_destructor(FunctionInstance *function) 642 { 643 } 644 645 static const function_vtbl_t NativeFunctionVtbl = { 646 NativeFunction_call, 647 NativeFunction_toString, 648 NativeFunction_destructor 649 }; 650 651 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name, 652 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret) 653 { 654 NativeFunction *function; 655 HRESULT hres; 656 657 hres = create_function(ctx, builtin_info, &NativeFunctionVtbl, sizeof(NativeFunction), flags, FALSE, NULL, (void**)&function); 658 if(FAILED(hres)) 659 return hres; 660 661 if(builtin_info) 662 hres = jsdisp_define_data_property(&function->function.dispex, lengthW, 0, 663 jsval_number(function->function.length)); 664 if(SUCCEEDED(hres)) 665 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, 0, jsval_obj(prototype)); 666 if(FAILED(hres)) { 667 jsdisp_release(&function->function.dispex); 668 return hres; 669 } 670 671 function->proc = value_proc; 672 function->name = name; 673 674 *ret = &function->function.dispex; 675 return S_OK; 676 } 677 678 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot) 679 { 680 static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0}; 681 682 return jsdisp_define_data_property(prot, constructorW, PROPF_WRITABLE | PROPF_CONFIGURABLE, 683 jsval_obj(constr)); 684 } 685 686 HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name, 687 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret) 688 { 689 jsdisp_t *constr; 690 HRESULT hres; 691 692 hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr); 693 if(FAILED(hres)) 694 return hres; 695 696 hres = set_constructor_prop(ctx, constr, prototype); 697 if(FAILED(hres)) { 698 jsdisp_release(constr); 699 return hres; 700 } 701 702 *ret = constr; 703 return S_OK; 704 } 705 706 static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags, 707 unsigned argc, jsval_t *argv, jsval_t *r) 708 { 709 InterpretedFunction *function = (InterpretedFunction*)func; 710 jsdisp_t *var_disp, *new_obj = NULL; 711 DWORD exec_flags = 0; 712 HRESULT hres; 713 714 TRACE("%p\n", function); 715 716 if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) { 717 WARN("Script engine state does not allow running code.\n"); 718 return E_UNEXPECTED; 719 } 720 721 if(flags & DISPATCH_CONSTRUCT) { 722 hres = create_object(ctx, &function->function.dispex, &new_obj); 723 if(FAILED(hres)) 724 return hres; 725 this_obj = to_disp(new_obj); 726 } 727 728 if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) 729 exec_flags |= EXEC_RETURN_TO_INTERP; 730 if(flags & DISPATCH_CONSTRUCT) 731 exec_flags |= EXEC_CONSTRUCTOR; 732 733 hres = create_dispex(ctx, NULL, NULL, &var_disp); 734 if(SUCCEEDED(hres)) 735 hres = exec_source(ctx, exec_flags, function->code, function->func_code, function->scope_chain, this_obj, 736 &function->function.dispex, var_disp, argc, argv, r); 737 if(new_obj) 738 jsdisp_release(new_obj); 739 740 jsdisp_release(var_disp); 741 return hres; 742 } 743 744 static HRESULT InterpretedFunction_toString(FunctionInstance *func, jsstr_t **ret) 745 { 746 InterpretedFunction *function = (InterpretedFunction*)func; 747 748 *ret = jsstr_alloc_len(function->func_code->source, function->func_code->source_len); 749 return *ret ? S_OK : E_OUTOFMEMORY; 750 } 751 752 static void InterpretedFunction_destructor(FunctionInstance *func) 753 { 754 InterpretedFunction *function = (InterpretedFunction*)func; 755 756 release_bytecode(function->code); 757 if(function->scope_chain) 758 scope_release(function->scope_chain); 759 } 760 761 static const function_vtbl_t InterpretedFunctionVtbl = { 762 InterpretedFunction_call, 763 InterpretedFunction_toString, 764 InterpretedFunction_destructor 765 }; 766 767 HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code, 768 scope_chain_t *scope_chain, jsdisp_t **ret) 769 { 770 InterpretedFunction *function; 771 jsdisp_t *prototype; 772 HRESULT hres; 773 774 hres = create_object(ctx, NULL, &prototype); 775 if(FAILED(hres)) 776 return hres; 777 778 hres = create_function(ctx, NULL, &InterpretedFunctionVtbl, sizeof(InterpretedFunction), PROPF_CONSTR, 779 FALSE, NULL, (void**)&function); 780 if(SUCCEEDED(hres)) { 781 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, PROPF_WRITABLE, 782 jsval_obj(prototype)); 783 if(SUCCEEDED(hres)) 784 hres = set_constructor_prop(ctx, &function->function.dispex, prototype); 785 if(FAILED(hres)) 786 jsdisp_release(&function->function.dispex); 787 } 788 jsdisp_release(prototype); 789 if(FAILED(hres)) 790 return hres; 791 792 if(scope_chain) { 793 scope_addref(scope_chain); 794 function->scope_chain = scope_chain; 795 } 796 797 bytecode_addref(code); 798 function->code = code; 799 function->func_code = func_code; 800 function->function.length = function->func_code->param_cnt; 801 802 *ret = &function->function.dispex; 803 return S_OK; 804 } 805 806 static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags, 807 unsigned argc, jsval_t *argv, jsval_t *r) 808 { 809 BindFunction *function = (BindFunction*)func; 810 jsval_t *call_args = NULL; 811 unsigned call_argc; 812 HRESULT hres; 813 814 TRACE("%p\n", function); 815 816 call_argc = function->argc + argc; 817 if(call_argc) { 818 call_args = heap_alloc(function->argc * sizeof(*function->args)); 819 if(!call_args) 820 return E_OUTOFMEMORY; 821 822 if(function->argc) 823 memcpy(call_args, function->args, function->argc * sizeof(*call_args)); 824 if(argc) 825 memcpy(call_args + function->argc, argv, argc * sizeof(*call_args)); 826 } 827 828 hres = function->target->vtbl->call(ctx, function->target, function->this, flags, call_argc, call_args, r); 829 830 heap_free(call_args); 831 return hres; 832 } 833 834 static HRESULT BindFunction_toString(FunctionInstance *function, jsstr_t **ret) 835 { 836 static const WCHAR native_functionW[] = 837 {'\n','f','u','n','c','t','i','o','n','(',')',' ','{','\n', 838 ' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n', 839 '}','\n',0}; 840 841 *ret = jsstr_alloc(native_functionW); 842 return *ret ? S_OK : E_OUTOFMEMORY; 843 } 844 845 static void BindFunction_destructor(FunctionInstance *func) 846 { 847 BindFunction *function = (BindFunction*)func; 848 unsigned i; 849 850 TRACE("%p\n", function); 851 852 for(i = 0; i < function->argc; i++) 853 jsval_release(function->args[i]); 854 jsdisp_release(&function->target->dispex); 855 IDispatch_Release(function->this); 856 } 857 858 static const function_vtbl_t BindFunctionVtbl = { 859 BindFunction_call, 860 BindFunction_toString, 861 BindFunction_destructor 862 }; 863 864 static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, IDispatch *bound_this, unsigned argc, 865 jsval_t *argv, jsdisp_t **ret) 866 { 867 BindFunction *function; 868 HRESULT hres; 869 870 hres = create_function(ctx, NULL, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD, 871 FALSE, NULL, (void**)&function); 872 if(FAILED(hres)) 873 return hres; 874 875 jsdisp_addref(&target->dispex); 876 function->target = target; 877 878 IDispatch_AddRef(function->this = bound_this); 879 880 for(function->argc = 0; function->argc < argc; function->argc++) { 881 hres = jsval_copy(argv[function->argc], function->args + function->argc); 882 if(FAILED(hres)) { 883 jsdisp_release(&function->function.dispex); 884 return hres; 885 } 886 } 887 888 function->function.length = target->length > argc ? target->length - argc : 0; 889 890 *ret = &function->function.dispex; 891 return S_OK; 892 } 893 894 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, IDispatch **ret) 895 { 896 WCHAR *str = NULL, *ptr; 897 unsigned len = 0, i = 0; 898 bytecode_t *code; 899 jsdisp_t *function; 900 jsstr_t **params = NULL; 901 int j = 0; 902 HRESULT hres = S_OK; 903 904 static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('}; 905 static const WCHAR function_beginW[] = {')',' ','{','\n'}; 906 static const WCHAR function_endW[] = {'\n','}',0}; 907 908 if(argc) { 909 params = heap_alloc(argc*sizeof(*params)); 910 if(!params) 911 return E_OUTOFMEMORY; 912 913 if(argc > 2) 914 len = (argc-2)*2; /* separating commas */ 915 for(i=0; i < argc; i++) { 916 hres = to_string(ctx, argv[i], params+i); 917 if(FAILED(hres)) 918 break; 919 len += jsstr_length(params[i]); 920 } 921 } 922 923 if(SUCCEEDED(hres)) { 924 len += ARRAY_SIZE(function_anonymousW) + ARRAY_SIZE(function_beginW) + ARRAY_SIZE(function_endW); 925 str = heap_alloc(len*sizeof(WCHAR)); 926 if(str) { 927 memcpy(str, function_anonymousW, sizeof(function_anonymousW)); 928 ptr = str + ARRAY_SIZE(function_anonymousW); 929 if(argc > 1) { 930 while(1) { 931 ptr += jsstr_flush(params[j], ptr); 932 if(++j == argc-1) 933 break; 934 *ptr++ = ','; 935 *ptr++ = ' '; 936 } 937 } 938 memcpy(ptr, function_beginW, sizeof(function_beginW)); 939 ptr += ARRAY_SIZE(function_beginW); 940 if(argc) 941 ptr += jsstr_flush(params[argc-1], ptr); 942 memcpy(ptr, function_endW, sizeof(function_endW)); 943 944 TRACE("%s\n", debugstr_w(str)); 945 }else { 946 hres = E_OUTOFMEMORY; 947 } 948 } 949 950 while(i) 951 jsstr_release(params[--i]); 952 heap_free(params); 953 if(FAILED(hres)) 954 return hres; 955 956 hres = compile_script(ctx, str, NULL, NULL, FALSE, FALSE, &code); 957 heap_free(str); 958 if(FAILED(hres)) 959 return hres; 960 961 if(code->global_code.func_cnt != 1 || code->global_code.var_cnt != 1) { 962 ERR("Invalid parser result!\n"); 963 release_bytecode(code); 964 return E_UNEXPECTED; 965 } 966 967 hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function); 968 release_bytecode(code); 969 if(FAILED(hres)) 970 return hres; 971 972 *ret = to_disp(function); 973 return S_OK; 974 } 975 976 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 977 jsval_t *r) 978 { 979 HRESULT hres; 980 981 TRACE("\n"); 982 983 switch(flags) { 984 case DISPATCH_METHOD: 985 case DISPATCH_CONSTRUCT: { 986 IDispatch *ret; 987 988 hres = construct_function(ctx, argc, argv, &ret); 989 if(FAILED(hres)) 990 return hres; 991 992 *r = jsval_disp(ret); 993 break; 994 } 995 default: 996 FIXME("unimplemented flags %x\n", flags); 997 return E_NOTIMPL; 998 } 999 1000 return S_OK; 1001 } 1002 1003 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1004 jsval_t *r) 1005 { 1006 FIXME("\n"); 1007 return E_NOTIMPL; 1008 } 1009 1010 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype) 1011 { 1012 NativeFunction *prot, *constr; 1013 HRESULT hres; 1014 1015 static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0}; 1016 1017 hres = create_function(ctx, &Function_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR, 1018 TRUE, object_prototype, (void**)&prot); 1019 if(FAILED(hres)) 1020 return hres; 1021 1022 prot->proc = FunctionProt_value; 1023 prot->name = prototypeW; 1024 1025 hres = create_function(ctx, &FunctionInst_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR|1, 1026 TRUE, &prot->function.dispex, (void**)&constr); 1027 if(SUCCEEDED(hres)) { 1028 constr->proc = FunctionConstr_value; 1029 constr->name = FunctionW; 1030 hres = jsdisp_define_data_property(&constr->function.dispex, prototypeW, 0, jsval_obj(&prot->function.dispex)); 1031 if(SUCCEEDED(hres)) 1032 hres = set_constructor_prop(ctx, &constr->function.dispex, &prot->function.dispex); 1033 if(FAILED(hres)) 1034 jsdisp_release(&constr->function.dispex); 1035 } 1036 jsdisp_release(&prot->function.dispex); 1037 if(FAILED(hres)) 1038 return hres; 1039 1040 ctx->function_constr = &constr->function.dispex; 1041 return S_OK; 1042 } 1043