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