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