jscript: Directly return error code instead of using throw_type_error where possible.
[wine.git] / dlls / jscript / function.c
blobbaa5eb10d7be5d30d0eab8b63ff4a2f8752547bd
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 _function_vtbl_t function_vtbl_t;
30 typedef struct {
31 jsdisp_t dispex;
32 const function_vtbl_t *vtbl;
33 DWORD flags;
34 DWORD length;
35 } FunctionInstance;
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 function_code_t* (*get_code)(FunctionInstance*);
41 void (*destructor)(FunctionInstance*);
44 typedef struct {
45 FunctionInstance function;
46 scope_chain_t *scope_chain;
47 bytecode_t *code;
48 function_code_t *func_code;
49 } InterpretedFunction;
51 typedef struct {
52 FunctionInstance function;
53 builtin_invoke_t proc;
54 const WCHAR *name;
55 } NativeFunction;
57 typedef struct {
58 FunctionInstance function;
59 FunctionInstance *target;
60 IDispatch *this;
61 unsigned argc;
62 jsval_t args[1];
63 } BindFunction;
65 typedef struct {
66 jsdisp_t jsdisp;
67 InterpretedFunction *function;
68 jsval_t *buf;
69 call_frame_t *frame;
70 unsigned argc;
71 } ArgumentsInstance;
73 static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,jsval_t*,jsdisp_t**r);
75 static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
77 return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex);
80 static inline FunctionInstance *function_from_vdisp(vdisp_t *vdisp)
82 return function_from_jsdisp(vdisp->u.jsdisp);
85 static inline FunctionInstance *function_this(vdisp_t *jsthis)
87 return is_vclass(jsthis, JSCLASS_FUNCTION) ? function_from_vdisp(jsthis) : NULL;
90 static inline ArgumentsInstance *arguments_from_jsdisp(jsdisp_t *jsdisp)
92 return CONTAINING_RECORD(jsdisp, ArgumentsInstance, jsdisp);
95 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
97 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
98 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
99 static const WCHAR applyW[] = {'a','p','p','l','y',0};
100 static const WCHAR bindW[] = {'b','i','n','d',0};
101 static const WCHAR callW[] = {'c','a','l','l',0};
102 static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0};
104 static HRESULT Arguments_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
105 jsval_t *r)
107 FIXME("\n");
108 return E_NOTIMPL;
111 static void Arguments_destructor(jsdisp_t *jsdisp)
113 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
115 TRACE("(%p)\n", arguments);
117 if(arguments->buf) {
118 unsigned i;
119 for(i = 0; i < arguments->argc; i++)
120 jsval_release(arguments->buf[i]);
121 heap_free(arguments->buf);
124 jsdisp_release(&arguments->function->function.dispex);
125 heap_free(arguments);
128 static unsigned Arguments_idx_length(jsdisp_t *jsdisp)
130 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
131 return arguments->argc;
134 static jsval_t *get_argument_ref(ArgumentsInstance *arguments, unsigned idx)
136 if(arguments->buf)
137 return arguments->buf + idx;
138 if(arguments->frame->base_scope->frame || idx >= arguments->frame->function->param_cnt)
139 return arguments->jsdisp.ctx->stack + arguments->frame->arguments_off + idx;
140 return NULL;
143 static HRESULT Arguments_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r)
145 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
146 jsval_t *ref;
148 TRACE("%p[%u]\n", arguments, idx);
150 if((ref = get_argument_ref(arguments, idx)))
151 return jsval_copy(*ref, r);
153 /* FIXME: Accessing by name won't work for duplicated argument names */
154 return jsdisp_propget_name(arguments->frame->base_scope->jsobj,
155 arguments->function->func_code->params[idx], r);
158 static HRESULT Arguments_idx_put(jsdisp_t *jsdisp, unsigned idx, jsval_t val)
160 ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp);
161 jsval_t *ref;
162 HRESULT hres;
164 TRACE("%p[%u] = %s\n", arguments, idx, debugstr_jsval(val));
166 if((ref = get_argument_ref(arguments, idx))) {
167 jsval_t copy;
168 hres = jsval_copy(val, &copy);
169 if(FAILED(hres))
170 return hres;
172 jsval_release(*ref);
173 *ref = copy;
174 return S_OK;
177 /* FIXME: Accessing by name won't work for duplicated argument names */
178 return jsdisp_propput_name(arguments->frame->base_scope->jsobj,
179 arguments->function->func_code->params[idx], val);
182 static const builtin_info_t Arguments_info = {
183 JSCLASS_ARGUMENTS,
184 {NULL, Arguments_value, 0},
185 0, NULL,
186 Arguments_destructor,
187 NULL,
188 Arguments_idx_length,
189 Arguments_idx_get,
190 Arguments_idx_put
193 HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame)
195 ArgumentsInstance *args;
196 HRESULT hres;
198 static const WCHAR caleeW[] = {'c','a','l','l','e','e',0};
200 args = heap_alloc_zero(sizeof(*args));
201 if(!args)
202 return E_OUTOFMEMORY;
204 hres = init_dispex_from_constr(&args->jsdisp, ctx, &Arguments_info, ctx->object_constr);
205 if(FAILED(hres)) {
206 heap_free(args);
207 return hres;
210 args->function = (InterpretedFunction*)function_from_jsdisp(jsdisp_addref(frame->function_instance));
211 args->argc = frame->argc;
212 args->frame = frame;
214 hres = jsdisp_define_data_property(&args->jsdisp, lengthW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
215 jsval_number(args->argc));
216 if(SUCCEEDED(hres))
217 hres = jsdisp_define_data_property(&args->jsdisp, caleeW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
218 jsval_obj(&args->function->function.dispex));
219 if(SUCCEEDED(hres))
220 hres = jsdisp_propput(frame->base_scope->jsobj, argumentsW, PROPF_WRITABLE, jsval_obj(&args->jsdisp));
221 if(FAILED(hres)) {
222 jsdisp_release(&args->jsdisp);
223 return hres;
226 frame->arguments_obj = &args->jsdisp;
227 return S_OK;
230 void detach_arguments_object(jsdisp_t *args_disp)
232 ArgumentsInstance *arguments = arguments_from_jsdisp(args_disp);
233 call_frame_t *frame = arguments->frame;
234 const BOOL on_stack = frame->base_scope->frame == frame;
235 HRESULT hres;
237 /* Reset arguments value to cut the reference cycle. Note that since all activation contexts have
238 * their own arguments property, it's impossible to use prototype's one during name lookup */
239 jsdisp_propput_name(frame->base_scope->jsobj, argumentsW, jsval_undefined());
240 arguments->frame = NULL;
242 /* Don't bother coppying arguments if call frame holds the last reference. */
243 if(arguments->jsdisp.ref > 1) {
244 arguments->buf = heap_alloc(arguments->argc * sizeof(*arguments->buf));
245 if(arguments->buf) {
246 int i;
248 for(i = 0; i < arguments->argc ; i++) {
249 if(on_stack || i >= frame->function->param_cnt)
250 hres = jsval_copy(arguments->jsdisp.ctx->stack[frame->arguments_off + i], arguments->buf+i);
251 else
252 hres = jsdisp_propget_name(frame->base_scope->jsobj, frame->function->params[i], arguments->buf+i);
253 if(FAILED(hres))
254 arguments->buf[i] = jsval_undefined();
256 }else {
257 ERR("out of memory\n");
258 arguments->argc = 0;
262 jsdisp_release(frame->arguments_obj);
265 HRESULT Function_invoke(jsdisp_t *func_this, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
267 FunctionInstance *function;
269 TRACE("func %p this %p\n", func_this, jsthis);
271 assert(is_class(func_this, JSCLASS_FUNCTION));
272 function = function_from_jsdisp(func_this);
274 return function->vtbl->call(function->dispex.ctx, function, jsthis, flags, argc, argv, r);
277 static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
279 TRACE("%p\n", jsthis);
281 *r = jsval_number(function_from_jsdisp(jsthis)->length);
282 return S_OK;
285 static HRESULT Function_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
286 jsval_t *r)
288 FunctionInstance *function;
289 jsstr_t *str;
290 HRESULT hres;
292 TRACE("\n");
294 if(!(function = function_this(jsthis)))
295 return JS_E_FUNCTION_EXPECTED;
297 hres = function->vtbl->toString(function, &str);
298 if(FAILED(hres))
299 return hres;
301 if(r)
302 *r = jsval_string(str);
303 else
304 jsstr_release(str);
305 return S_OK;
308 static HRESULT array_to_args(script_ctx_t *ctx, jsdisp_t *arg_array, unsigned *argc, jsval_t **ret)
310 jsval_t *argv, val;
311 DWORD length, i;
312 HRESULT hres;
314 hres = jsdisp_propget_name(arg_array, lengthW, &val);
315 if(FAILED(hres))
316 return hres;
318 hres = to_uint32(ctx, val, &length);
319 jsval_release(val);
320 if(FAILED(hres))
321 return hres;
323 argv = heap_alloc(length * sizeof(*argv));
324 if(!argv)
325 return E_OUTOFMEMORY;
327 for(i=0; i<length; i++) {
328 hres = jsdisp_get_idx(arg_array, i, argv+i);
329 if(hres == DISP_E_UNKNOWNNAME) {
330 argv[i] = jsval_undefined();
331 }else if(FAILED(hres)) {
332 while(i--)
333 jsval_release(argv[i]);
334 heap_free(argv);
335 return hres;
339 *argc = length;
340 *ret = argv;
341 return S_OK;
344 static HRESULT Function_apply(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
346 FunctionInstance *function;
347 jsval_t *args = NULL;
348 unsigned i, cnt = 0;
349 IDispatch *this_obj = NULL;
350 HRESULT hres = S_OK;
352 TRACE("\n");
354 if(!(function = function_this(jsthis)) && (jsthis->flags & VDISP_JSDISP))
355 return JS_E_FUNCTION_EXPECTED;
357 if(argc) {
358 if(!is_undefined(argv[0]) && !is_null(argv[0])) {
359 hres = to_object(ctx, argv[0], &this_obj);
360 if(FAILED(hres))
361 return hres;
365 if(argc >= 2) {
366 jsdisp_t *arg_array = NULL;
368 if(is_object_instance(argv[1])) {
369 arg_array = iface_to_jsdisp(get_object(argv[1]));
370 if(arg_array &&
371 (!is_class(arg_array, JSCLASS_ARRAY) && !is_class(arg_array, JSCLASS_ARGUMENTS) )) {
372 jsdisp_release(arg_array);
373 arg_array = NULL;
377 if(arg_array) {
378 hres = array_to_args(ctx, arg_array, &cnt, &args);
379 jsdisp_release(arg_array);
380 }else {
381 FIXME("throw TypeError\n");
382 hres = E_FAIL;
386 if(SUCCEEDED(hres)) {
387 if(function) {
388 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, args, r);
389 }else {
390 jsval_t res;
391 hres = disp_call_value(ctx, jsthis->u.disp, this_obj, DISPATCH_METHOD, cnt, args, &res);
392 if(SUCCEEDED(hres)) {
393 if(r)
394 *r = res;
395 else
396 jsval_release(res);
401 if(this_obj)
402 IDispatch_Release(this_obj);
403 for(i=0; i < cnt; i++)
404 jsval_release(args[i]);
405 heap_free(args);
406 return hres;
409 static HRESULT Function_call(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
410 jsval_t *r)
412 FunctionInstance *function;
413 IDispatch *this_obj = NULL;
414 unsigned cnt = 0;
415 HRESULT hres;
417 TRACE("\n");
419 if(!(function = function_this(jsthis)))
420 return JS_E_FUNCTION_EXPECTED;
422 if(argc) {
423 if(!is_undefined(argv[0]) && !is_null(argv[0])) {
424 hres = to_object(ctx, argv[0], &this_obj);
425 if(FAILED(hres))
426 return hres;
429 cnt = argc-1;
432 hres = function->vtbl->call(ctx, function, this_obj, flags, cnt, argv + 1, r);
434 if(this_obj)
435 IDispatch_Release(this_obj);
436 return hres;
439 static HRESULT Function_bind(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
440 jsval_t *r)
442 FunctionInstance *function;
443 jsdisp_t *new_function;
444 HRESULT hres;
446 TRACE("\n");
448 if(!(function = function_this(jsthis)))
449 return JS_E_FUNCTION_EXPECTED;
451 if(argc < 1) {
452 FIXME("no this argument\n");
453 return E_NOTIMPL;
456 if(!is_object_instance(argv[0]) || !get_object(argv[0])) {
457 FIXME("%s is not an object instance\n", debugstr_jsval(argv[0]));
458 return E_NOTIMPL;
461 hres = create_bind_function(ctx, function, get_object(argv[0]), argc - 1, argv + 1, &new_function);
462 if(FAILED(hres))
463 return hres;
465 if(r)
466 *r = jsval_obj(new_function);
467 else
468 jsdisp_release(new_function);
469 return S_OK;
472 HRESULT Function_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
473 jsval_t *r)
475 FunctionInstance *function;
477 TRACE("\n");
479 if(!is_vclass(jsthis, JSCLASS_FUNCTION)) {
480 ERR("dispex is not a function\n");
481 return E_FAIL;
484 function = function_from_jsdisp(jsthis->u.jsdisp);
485 return function->vtbl->call(ctx, function, NULL, flags, argc, argv, r);
488 HRESULT Function_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
490 FunctionInstance *function = function_from_jsdisp(jsthis);
491 jsstr_t *str;
492 HRESULT hres;
494 TRACE("\n");
496 hres = function->vtbl->toString(function, &str);
497 if(FAILED(hres))
498 return hres;
500 *r = jsval_string(str);
501 return S_OK;
504 static HRESULT Function_get_arguments(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
506 FunctionInstance *function = function_from_jsdisp(jsthis);
507 call_frame_t *frame;
508 HRESULT hres;
510 TRACE("\n");
512 for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) {
513 if(frame->function_instance == &function->dispex) {
514 if(!frame->arguments_obj) {
515 hres = setup_arguments_object(ctx, frame);
516 if(FAILED(hres))
517 return hres;
519 *r = jsval_obj(jsdisp_addref(frame->arguments_obj));
520 return S_OK;
524 *r = jsval_null();
525 return S_OK;
528 function_code_t *Function_get_code(jsdisp_t *jsthis)
530 FunctionInstance *function;
532 assert(is_class(jsthis, JSCLASS_FUNCTION));
533 function = function_from_jsdisp(jsthis);
535 return function->vtbl->get_code(function);
538 static void Function_destructor(jsdisp_t *dispex)
540 FunctionInstance *function = function_from_jsdisp(dispex);
541 function->vtbl->destructor(function);
542 heap_free(function);
545 static const builtin_prop_t Function_props[] = {
546 {applyW, Function_apply, PROPF_METHOD|2},
547 {argumentsW, NULL, 0, Function_get_arguments},
548 {bindW, Function_bind, PROPF_METHOD|PROPF_ES5|1},
549 {callW, Function_call, PROPF_METHOD|1},
550 {lengthW, NULL, 0, Function_get_length},
551 {toStringW, Function_toString, PROPF_METHOD}
554 static const builtin_info_t Function_info = {
555 JSCLASS_FUNCTION,
556 DEFAULT_FUNCTION_VALUE,
557 ARRAY_SIZE(Function_props),
558 Function_props,
559 Function_destructor,
560 NULL
563 static const builtin_prop_t FunctionInst_props[] = {
564 {argumentsW, NULL, 0, Function_get_arguments},
565 {lengthW, NULL, 0, Function_get_length}
568 static const builtin_info_t FunctionInst_info = {
569 JSCLASS_FUNCTION,
570 DEFAULT_FUNCTION_VALUE,
571 ARRAY_SIZE(FunctionInst_props),
572 FunctionInst_props,
573 Function_destructor,
574 NULL
577 static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, const function_vtbl_t *vtbl, size_t size,
578 DWORD flags, BOOL funcprot, jsdisp_t *prototype, void **ret)
580 FunctionInstance *function;
581 HRESULT hres;
583 function = heap_alloc_zero(size);
584 if(!function)
585 return E_OUTOFMEMORY;
587 if(funcprot)
588 hres = init_dispex(&function->dispex, ctx, builtin_info, prototype);
589 else if(builtin_info)
590 hres = init_dispex_from_constr(&function->dispex, ctx, builtin_info, ctx->function_constr);
591 else
592 hres = init_dispex_from_constr(&function->dispex, ctx, &FunctionInst_info, ctx->function_constr);
593 if(FAILED(hres)) {
594 heap_free(function);
595 return hres;
598 function->vtbl = vtbl;
599 function->flags = flags;
600 function->length = flags & PROPF_ARGMASK;
602 *ret = function;
603 return S_OK;
606 static HRESULT NativeFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_disp, unsigned flags,
607 unsigned argc, jsval_t *argv, jsval_t *r)
609 NativeFunction *function = (NativeFunction*)func;
610 vdisp_t vthis;
611 HRESULT hres;
613 if(this_disp)
614 set_disp(&vthis, this_disp);
615 else if(ctx->host_global)
616 set_disp(&vthis, ctx->host_global);
617 else
618 set_jsdisp(&vthis, ctx->global);
620 hres = function->proc(ctx, &vthis, flags & ~DISPATCH_JSCRIPT_INTERNAL_MASK, argc, argv, r);
622 vdisp_release(&vthis);
623 return hres;
626 static HRESULT NativeFunction_toString(FunctionInstance *func, jsstr_t **ret)
628 NativeFunction *function = (NativeFunction*)func;
629 DWORD name_len;
630 jsstr_t *str;
631 WCHAR *ptr;
633 static const WCHAR native_prefixW[] = {'\n','f','u','n','c','t','i','o','n',' '};
634 static const WCHAR native_suffixW[] =
635 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
637 name_len = function->name ? lstrlenW(function->name) : 0;
638 str = jsstr_alloc_buf(ARRAY_SIZE(native_prefixW) + ARRAY_SIZE(native_suffixW) + name_len, &ptr);
639 if(!str)
640 return E_OUTOFMEMORY;
642 memcpy(ptr, native_prefixW, sizeof(native_prefixW));
643 ptr += ARRAY_SIZE(native_prefixW);
644 memcpy(ptr, function->name, name_len*sizeof(WCHAR));
645 ptr += name_len;
646 memcpy(ptr, native_suffixW, sizeof(native_suffixW));
648 *ret = str;
649 return S_OK;
652 static function_code_t *NativeFunction_get_code(FunctionInstance *function)
654 return NULL;
657 static void NativeFunction_destructor(FunctionInstance *function)
661 static const function_vtbl_t NativeFunctionVtbl = {
662 NativeFunction_call,
663 NativeFunction_toString,
664 NativeFunction_get_code,
665 NativeFunction_destructor
668 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
669 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
671 NativeFunction *function;
672 HRESULT hres;
674 hres = create_function(ctx, builtin_info, &NativeFunctionVtbl, sizeof(NativeFunction), flags, FALSE, NULL, (void**)&function);
675 if(FAILED(hres))
676 return hres;
678 if(builtin_info)
679 hres = jsdisp_define_data_property(&function->function.dispex, lengthW, 0,
680 jsval_number(function->function.length));
681 if(SUCCEEDED(hres))
682 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, 0, jsval_obj(prototype));
683 if(FAILED(hres)) {
684 jsdisp_release(&function->function.dispex);
685 return hres;
688 function->proc = value_proc;
689 function->name = name;
691 *ret = &function->function.dispex;
692 return S_OK;
695 static HRESULT set_constructor_prop(script_ctx_t *ctx, jsdisp_t *constr, jsdisp_t *prot)
697 static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r',0};
699 return jsdisp_define_data_property(prot, constructorW, PROPF_WRITABLE | PROPF_CONFIGURABLE,
700 jsval_obj(constr));
703 HRESULT create_builtin_constructor(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name,
704 const builtin_info_t *builtin_info, DWORD flags, jsdisp_t *prototype, jsdisp_t **ret)
706 jsdisp_t *constr;
707 HRESULT hres;
709 hres = create_builtin_function(ctx, value_proc, name, builtin_info, flags, prototype, &constr);
710 if(FAILED(hres))
711 return hres;
713 hres = set_constructor_prop(ctx, constr, prototype);
714 if(FAILED(hres)) {
715 jsdisp_release(constr);
716 return hres;
719 *ret = constr;
720 return S_OK;
723 static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
724 unsigned argc, jsval_t *argv, jsval_t *r)
726 InterpretedFunction *function = (InterpretedFunction*)func;
727 jsdisp_t *var_disp, *new_obj = NULL;
728 DWORD exec_flags = 0;
729 HRESULT hres;
731 TRACE("%p\n", function);
733 if(ctx->state == SCRIPTSTATE_UNINITIALIZED || ctx->state == SCRIPTSTATE_CLOSED) {
734 WARN("Script engine state does not allow running code.\n");
735 return E_UNEXPECTED;
738 if(flags & DISPATCH_CONSTRUCT) {
739 hres = create_object(ctx, &function->function.dispex, &new_obj);
740 if(FAILED(hres))
741 return hres;
742 this_obj = to_disp(new_obj);
745 if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE)
746 exec_flags |= EXEC_RETURN_TO_INTERP;
747 if(flags & DISPATCH_CONSTRUCT)
748 exec_flags |= EXEC_CONSTRUCTOR;
750 hres = create_dispex(ctx, NULL, NULL, &var_disp);
751 if(SUCCEEDED(hres))
752 hres = exec_source(ctx, exec_flags, function->code, function->func_code, function->scope_chain, this_obj,
753 &function->function.dispex, var_disp, argc, argv, r);
754 if(new_obj)
755 jsdisp_release(new_obj);
757 jsdisp_release(var_disp);
758 return hres;
761 static HRESULT InterpretedFunction_toString(FunctionInstance *func, jsstr_t **ret)
763 InterpretedFunction *function = (InterpretedFunction*)func;
765 *ret = jsstr_alloc_len(function->func_code->source, function->func_code->source_len);
766 return *ret ? S_OK : E_OUTOFMEMORY;
769 static function_code_t *InterpretedFunction_get_code(FunctionInstance *func)
771 InterpretedFunction *function = (InterpretedFunction*)func;
773 return function->func_code;
776 static void InterpretedFunction_destructor(FunctionInstance *func)
778 InterpretedFunction *function = (InterpretedFunction*)func;
780 release_bytecode(function->code);
781 if(function->scope_chain)
782 scope_release(function->scope_chain);
785 static const function_vtbl_t InterpretedFunctionVtbl = {
786 InterpretedFunction_call,
787 InterpretedFunction_toString,
788 InterpretedFunction_get_code,
789 InterpretedFunction_destructor
792 HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code,
793 scope_chain_t *scope_chain, jsdisp_t **ret)
795 InterpretedFunction *function;
796 jsdisp_t *prototype;
797 HRESULT hres;
799 hres = create_object(ctx, NULL, &prototype);
800 if(FAILED(hres))
801 return hres;
803 hres = create_function(ctx, NULL, &InterpretedFunctionVtbl, sizeof(InterpretedFunction), PROPF_CONSTR,
804 FALSE, NULL, (void**)&function);
805 if(SUCCEEDED(hres)) {
806 hres = jsdisp_define_data_property(&function->function.dispex, prototypeW, PROPF_WRITABLE,
807 jsval_obj(prototype));
808 if(SUCCEEDED(hres))
809 hres = set_constructor_prop(ctx, &function->function.dispex, prototype);
810 if(FAILED(hres))
811 jsdisp_release(&function->function.dispex);
813 jsdisp_release(prototype);
814 if(FAILED(hres))
815 return hres;
817 if(scope_chain) {
818 scope_addref(scope_chain);
819 function->scope_chain = scope_chain;
822 bytecode_addref(code);
823 function->code = code;
824 function->func_code = func_code;
825 function->function.length = function->func_code->param_cnt;
827 *ret = &function->function.dispex;
828 return S_OK;
831 static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, IDispatch *this_obj, unsigned flags,
832 unsigned argc, jsval_t *argv, jsval_t *r)
834 BindFunction *function = (BindFunction*)func;
835 jsval_t *call_args = NULL;
836 unsigned call_argc;
837 HRESULT hres;
839 TRACE("%p\n", function);
841 call_argc = function->argc + argc;
842 if(call_argc) {
843 call_args = heap_alloc(function->argc * sizeof(*function->args));
844 if(!call_args)
845 return E_OUTOFMEMORY;
847 if(function->argc)
848 memcpy(call_args, function->args, function->argc * sizeof(*call_args));
849 if(argc)
850 memcpy(call_args + function->argc, argv, argc * sizeof(*call_args));
853 hres = function->target->vtbl->call(ctx, function->target, function->this, flags, call_argc, call_args, r);
855 heap_free(call_args);
856 return hres;
859 static HRESULT BindFunction_toString(FunctionInstance *function, jsstr_t **ret)
861 static const WCHAR native_functionW[] =
862 {'\n','f','u','n','c','t','i','o','n','(',')',' ','{','\n',
863 ' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n',
864 '}','\n',0};
866 *ret = jsstr_alloc(native_functionW);
867 return *ret ? S_OK : E_OUTOFMEMORY;
870 static function_code_t *BindFunction_get_code(FunctionInstance *function)
872 return NULL;
875 static void BindFunction_destructor(FunctionInstance *func)
877 BindFunction *function = (BindFunction*)func;
878 unsigned i;
880 TRACE("%p\n", function);
882 for(i = 0; i < function->argc; i++)
883 jsval_release(function->args[i]);
884 jsdisp_release(&function->target->dispex);
885 IDispatch_Release(function->this);
888 static const function_vtbl_t BindFunctionVtbl = {
889 BindFunction_call,
890 BindFunction_toString,
891 BindFunction_get_code,
892 BindFunction_destructor
895 static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, IDispatch *bound_this, unsigned argc,
896 jsval_t *argv, jsdisp_t **ret)
898 BindFunction *function;
899 HRESULT hres;
901 hres = create_function(ctx, NULL, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD,
902 FALSE, NULL, (void**)&function);
903 if(FAILED(hres))
904 return hres;
906 jsdisp_addref(&target->dispex);
907 function->target = target;
909 IDispatch_AddRef(function->this = bound_this);
911 for(function->argc = 0; function->argc < argc; function->argc++) {
912 hres = jsval_copy(argv[function->argc], function->args + function->argc);
913 if(FAILED(hres)) {
914 jsdisp_release(&function->function.dispex);
915 return hres;
919 function->function.length = target->length > argc ? target->length - argc : 0;
921 *ret = &function->function.dispex;
922 return S_OK;
925 static HRESULT construct_function(script_ctx_t *ctx, unsigned argc, jsval_t *argv, IDispatch **ret)
927 WCHAR *str = NULL, *ptr;
928 unsigned len = 0, i = 0;
929 bytecode_t *code;
930 jsdisp_t *function;
931 jsstr_t **params = NULL;
932 int j = 0;
933 HRESULT hres = S_OK;
935 static const WCHAR function_anonymousW[] = {'f','u','n','c','t','i','o','n',' ','a','n','o','n','y','m','o','u','s','('};
936 static const WCHAR function_beginW[] = {')',' ','{','\n'};
937 static const WCHAR function_endW[] = {'\n','}',0};
939 if(argc) {
940 params = heap_alloc(argc*sizeof(*params));
941 if(!params)
942 return E_OUTOFMEMORY;
944 if(argc > 2)
945 len = (argc-2)*2; /* separating commas */
946 for(i=0; i < argc; i++) {
947 hres = to_string(ctx, argv[i], params+i);
948 if(FAILED(hres))
949 break;
950 len += jsstr_length(params[i]);
954 if(SUCCEEDED(hres)) {
955 len += ARRAY_SIZE(function_anonymousW) + ARRAY_SIZE(function_beginW) + ARRAY_SIZE(function_endW);
956 str = heap_alloc(len*sizeof(WCHAR));
957 if(str) {
958 memcpy(str, function_anonymousW, sizeof(function_anonymousW));
959 ptr = str + ARRAY_SIZE(function_anonymousW);
960 if(argc > 1) {
961 while(1) {
962 ptr += jsstr_flush(params[j], ptr);
963 if(++j == argc-1)
964 break;
965 *ptr++ = ',';
966 *ptr++ = ' ';
969 memcpy(ptr, function_beginW, sizeof(function_beginW));
970 ptr += ARRAY_SIZE(function_beginW);
971 if(argc)
972 ptr += jsstr_flush(params[argc-1], ptr);
973 memcpy(ptr, function_endW, sizeof(function_endW));
975 TRACE("%s\n", debugstr_w(str));
976 }else {
977 hres = E_OUTOFMEMORY;
981 while(i)
982 jsstr_release(params[--i]);
983 heap_free(params);
984 if(FAILED(hres))
985 return hres;
987 hres = compile_script(ctx, str, 0, 0, NULL, NULL, FALSE, FALSE, &code);
988 heap_free(str);
989 if(FAILED(hres))
990 return hres;
992 if(code->global_code.func_cnt != 1 || code->global_code.var_cnt != 1) {
993 ERR("Invalid parser result!\n");
994 release_bytecode(code);
995 return E_UNEXPECTED;
998 hres = create_source_function(ctx, code, code->global_code.funcs, NULL, &function);
999 release_bytecode(code);
1000 if(FAILED(hres))
1001 return hres;
1003 *ret = to_disp(function);
1004 return S_OK;
1007 static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1008 jsval_t *r)
1010 HRESULT hres;
1012 TRACE("\n");
1014 switch(flags) {
1015 case DISPATCH_METHOD:
1016 case DISPATCH_CONSTRUCT: {
1017 IDispatch *ret;
1019 hres = construct_function(ctx, argc, argv, &ret);
1020 if(FAILED(hres))
1021 return hres;
1023 *r = jsval_disp(ret);
1024 break;
1026 default:
1027 FIXME("unimplemented flags %x\n", flags);
1028 return E_NOTIMPL;
1031 return S_OK;
1034 static HRESULT FunctionProt_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1035 jsval_t *r)
1037 FIXME("\n");
1038 return E_NOTIMPL;
1041 HRESULT init_function_constr(script_ctx_t *ctx, jsdisp_t *object_prototype)
1043 NativeFunction *prot, *constr;
1044 HRESULT hres;
1046 static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
1048 hres = create_function(ctx, &Function_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR,
1049 TRUE, object_prototype, (void**)&prot);
1050 if(FAILED(hres))
1051 return hres;
1053 prot->proc = FunctionProt_value;
1054 prot->name = prototypeW;
1056 hres = create_function(ctx, &FunctionInst_info, &NativeFunctionVtbl, sizeof(NativeFunction), PROPF_CONSTR|1,
1057 TRUE, &prot->function.dispex, (void**)&constr);
1058 if(SUCCEEDED(hres)) {
1059 constr->proc = FunctionConstr_value;
1060 constr->name = FunctionW;
1061 hres = jsdisp_define_data_property(&constr->function.dispex, prototypeW, 0, jsval_obj(&prot->function.dispex));
1062 if(SUCCEEDED(hres))
1063 hres = set_constructor_prop(ctx, &constr->function.dispex, &prot->function.dispex);
1064 if(FAILED(hres))
1065 jsdisp_release(&constr->function.dispex);
1067 jsdisp_release(&prot->function.dispex);
1068 if(FAILED(hres))
1069 return hres;
1071 ctx->function_constr = &constr->function.dispex;
1072 return S_OK;