dpnet/tests: A spelling fix in a variable name.
[wine.git] / dlls / jscript / function.c
blob8480a3bf1a06e79c4ea8f0ae135293a9b2d2daee
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <assert.h>
21 #include "jscript.h"
22 #include "engine.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
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;
39 typedef struct {
40 jsdisp_t jsdisp;
41 FunctionInstance *function;
42 jsval_t *buf;
43 call_frame_t *frame;
44 unsigned argc;
45 } ArgumentsInstance;
47 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
49 return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex);
52 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
54 return function_from_jsdisp(vdisp->u.jsdisp);
57 static inline FunctionInstance *function_this(vdisp_t *jsthis)
59 return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
62 static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp)
64 return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp);
67 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
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};
75 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
76 jsval_t *r)
78 FIXME("\n");
79 return E_NOTIMPL;
82 static void Arguments_destructor(jsdisp_t *jsdisp)
84 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
86 TRACE("(%p)\n", arguments);
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);
95 jsdisp_release(&arguments->function->dispex);
96 heap_free(arguments);
99 static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
101 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
102 return arguments->argc;
105 static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx)
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;
114 static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
116 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
117 jsval_t *ref;
119 TRACE("%p[%u]\n", arguments, idx);
121 if((ref = get_argument_ref(arguments, idx)))
122 return jsval_copy(*ref, r);
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);
128 static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
130 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
131 jsval_t *ref;
132 HRESULT hres;
134 TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
136 if((ref = get_argument_ref(arguments, idx))) {
137 jsval_t copy;
138 hres = jsval_copy(val, &copy);
139 if(FAILED(hres))
140 return hres;
142 jsval_release(*ref);
143 *ref = copy;
144 return S_OK;
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);
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
162 HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame)
164 ArgumentsInstance *args;
165 HRESULT hres;
167 static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
169 args = heap_alloc_zero(sizeof(*args));
170 if(!args)
171 return E_OUTOFMEMORY;
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;
179 args->function = function_from_jsdisp(jsdisp_addref(frame->function_instance));
180 args->argc = frame->argc;
181 args->frame = frame;
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;
193 frame->arguments_obj = &args->jsdisp;
194 return S_OK;
197 void detach_arguments_object(jsdisp_t *args_disp)
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;
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;
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;
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();
223 }else {
224 ERR("out of memory\n");
225 arguments->argc = 0;
229 jsdisp_release(frame->arguments_obj);
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)
235 jsdisp_t *var_disp;
236 DWORD exec_flags = 0;
237 HRESULT hres;
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;
244 if(!function->func_code) {
245 FIXME("no source\n");
246 return E_FAIL;
249 hres = create_dispex(ctx, NULL, NULL, &var_disp);
250 if(FAILED(hres))
251 return hres;
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);
260 jsdisp_release(var_disp);
261 return hres;
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)
267 vdisp_t vthis;
268 HRESULT hres;
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);
277 hres = function->value_proc(ctx, &vthis, flags, argc, argv, r);
279 vdisp_release(&vthis);
280 return hres;
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)
286 if(function->value_proc)
287 return invoke_value_proc(ctx, function, this_obj, DISPATCH_METHOD, argc, argv, r);
289 return invoke_source(ctx, function, this_obj, argc, argv, FALSE, caller_execs_source, r);
292 static HRESULT function_to_string(FunctionInstance *function, jsstr_t **ret)
294 jsstr_t *str;
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'};
300 if(function->value_proc) {
301 DWORD name_len;
302 WCHAR *ptr;
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;
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;
318 *ret = str;
319 return S_OK;
322 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
324 const BOOL caller_execs_source = (flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) != 0;
325 FunctionInstance *function;
327 TRACE("func %p this %p\n", func_this, jsthis);
329 assert(is_class(func_this, JSCLASS_FUNCTION));
330 function = function_from_jsdisp(func_this);
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);
336 if(flags == DISPATCH_CONSTRUCT) {
337 jsdisp_t *this_obj;
338 HRESULT hres;
340 hres = create_object(function->dispex.ctx, &function->dispex, &this_obj);
341 if(FAILED(hres))
342 return hres;
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;
349 assert(flags == DISPATCH_METHOD);
350 return invoke_source(function->dispex.ctx, function, jsthis, argc, argv, FALSE, caller_execs_source, r);
353 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
355 TRACE("%p\n", jsthis);
357 *r = jsval_number(function_from_jsdisp(jsthis)->length);
358 return S_OK;
361 static HRESULT Function_set_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
363 FIXME("\n");
364 return E_NOTIMPL;
367 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
368 jsval_t *r)
370 FunctionInstance *function;
371 jsstr_t *str;
372 HRESULT hres;
374 TRACE("\n");
376 if(!(function = function_this(jsthis)))
377 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
379 hres = function_to_string(function, &str);
380 if(FAILED(hres))
381 return hres;
383 if(r)
384 *r = jsval_string(str);
385 else
386 jsstr_release(str);
387 return S_OK;
390 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret)
392 jsval_t *argv, val;
393 DWORD length, i;
394 HRESULT hres;
396 hres = jsdisp_propget_name(arg_array, lengthW, &val);
397 if(FAILED(hres))
398 return hres;
400 hres = to_uint32(ctx, val, &length);
401 jsval_release(val);
402 if(FAILED(hres))
403 return hres;
405 argv = heap_alloc(length * sizeof(*argv));
406 if(!argv)
407 return E_OUTOFMEMORY;
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;
421 *argc = length;
422 *ret = argv;
423 return S_OK;
426 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
428 FunctionInstance *function;
429 jsval_t *args = NULL;
430 unsigned i, cnt = 0;
431 IDispatch *this_obj = NULL;
432 HRESULT hres = S_OK;
434 TRACE("\n");
436 if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP))
437 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
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;
447 if(argc >= 2) {
448 jsdisp_t *arg_array = NULL;
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;
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;
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);
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;
491 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
492 jsval_t *r)
494 FunctionInstance *function;
495 IDispatch *this_obj = NULL;
496 unsigned cnt = 0;
497 HRESULT hres;
499 TRACE("\n");
501 if(!(function = function_this(jsthis)))
502 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
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;
511 cnt = argc-1;
514 hres = call_function(ctx, function, this_obj, cnt, argv+1, (flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE) != 0, r);
516 if(this_obj)
517 IDispatch_Release(this_obj);
518 return hres;
521 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
522 jsval_t *r)
524 FunctionInstance *function;
526 TRACE("\n");
528 if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
529 ERR("dispex is not a function\n");
530 return E_FAIL;
533 function = function_from_jsdisp(jsthis->u.jsdisp);
535 assert(function->value_proc != NULL);
536 return invoke_value_proc(ctx, function, NULL, flags, argc, argv, r);
539 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
541 jsstr_t *str;
542 HRESULT hres;
544 TRACE("\n");
546 hres = function_to_string(function_from_jsdisp(jsthis), &str);
547 if(FAILED(hres))
548 return hres;
550 *r = jsval_string(str);
551 return S_OK;
554 static HRESULT Function_get_arguments(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
556 FunctionInstance *function = function_from_jsdisp(jsthis);
557 call_frame_t *frame;
558 HRESULT hres;
560 TRACE("\n");
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;
569 *r = jsval_obj(jsdisp_addref(frame->arguments_obj));
570 return S_OK;
574 *r = jsval_null();
575 return S_OK;
578 static void Function_destructor(jsdisp_t *dispex)
580 FunctionInstance *This = function_from_jsdisp(dispex);
582 if(This->code)
583 release_bytecode(This->code);
584 if(This->scope_chain)
585 scope_release(This->scope_chain);
586 heap_free(This);
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}
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
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}
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
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)
623 FunctionInstance *function;
624 HRESULT hres;
626 function = heap_alloc_zero(sizeof(FunctionInstance));
627 if(!function)
628 return E_OUTOFMEMORY;
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;
641 function->flags = flags;
642 function->length = flags & PROPF_ARGMASK;
644 *ret = function;
645 return S_OK;
648 static inline HRESULT set_prototype(script_ctx_t *ctx, jsdisp_t *dispex, jsdisp_t *prototype)
650 return jsdisp_propput_dontenum(dispex, prototypeW, jsval_obj(prototype));
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)
656 FunctionInstance *function;
657 HRESULT hres;
659 hres = create_function(ctx, builtin_info, flags, FALSE, NULL, &function);
660 if(FAILED(hres))
661 return hres;
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;
672 function->value_proc = value_proc;
673 function->name = name;
675 *ret = &function->dispex;
676 return S_OK;
679 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot)
681 static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0};
683 return jsdisp_propput_dontenum(prot, constructorW, jsval_obj(constr));
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)
689 jsdisp_t *constr;
690 HRESULT hres;
692 hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr);
693 if(FAILED(hres))
694 return hres;
696 hres = set_constructor_prop(ctx, constr, prototype);
697 if(FAILED(hres)) {
698 jsdisp_release(constr);
699 return hres;
702 *ret = constr;
703 return S_OK;
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)
709 FunctionInstance *function;
710 jsdisp_t *prototype;
711 HRESULT hres;
713 hres = create_object(ctx, NULL, &prototype);
714 if(FAILED(hres))
715 return hres;
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);
725 jsdisp_release(prototype);
726 if(FAILED(hres))
727 return hres;
729 if(scope_chain) {
730 scope_addref(scope_chain);
731 function->scope_chain = scope_chain;
734 bytecode_addref(code);
735 function->code = code;
736 function->func_code = func_code;
737 function->length = function->func_code->param_cnt;
739 *ret = &function->dispex;
740 return S_OK;
743 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, IDispatch **ret)
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;
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};
757 if(argc) {
758 params = heap_alloc(argc*sizeof(*params));
759 if(!params)
760 return E_OUTOFMEMORY;
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]);
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++ = ' ';
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));
793 TRACE("%s\n", debugstr_w(str));
794 }else {
795 hres = E_OUTOFMEMORY;
799 while(i)
800 jsstr_release(params[--i]);
801 heap_free(params);
802 if(FAILED(hres))
803 return hres;
805 hres = compile_script(ctx, str, NULL, NULL, FALSE, FALSE, &code);
806 heap_free(str);
807 if(FAILED(hres))
808 return hres;
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;
816 hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function);
817 release_bytecode(code);
818 if(FAILED(hres))
819 return hres;
821 *ret = to_disp(function);
822 return S_OK;
825 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
826 jsval_t *r)
828 HRESULT hres;
830 TRACE("\n");
832 switch(flags) {
833 case DISPATCH_METHOD:
834 case DISPATCH_CONSTRUCT: {
835 IDispatch *ret;
837 hres = construct_function(ctx, argc, argv, &ret);
838 if(FAILED(hres))
839 return hres;
841 *r = jsval_disp(ret);
842 break;
844 default:
845 FIXME("unimplemented flags %x\n", flags);
846 return E_NOTIMPL;
849 return S_OK;
852 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
853 jsval_t *r)
855 FIXME("\n");
856 return E_NOTIMPL;
859 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
861 FunctionInstance *prot, *constr;
862 HRESULT hres;
864 static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
866 hres = create_function(ctx, &Function_info, PROPF_CONSTR, TRUE, object_prototype, &prot);
867 if(FAILED(hres))
868 return hres;
870 prot->value_proc = FunctionProt_value;
871 prot->name = prototypeW;
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);
883 jsdisp_release(&prot->dispex);
884 if(FAILED(hres))
885 return hres;
887 ctx->function_constr = &constr->dispex;
888 return S_OK;