jscript: Added Function.[[call]] implementation.
[wine] / dlls / jscript / function.c
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 "jscript.h"
20 #include "engine.h"
21
22 #include "wine/debug.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
25
26 typedef struct {
27     DispatchEx dispex;
28     builtin_invoke_t value_proc;
29     DWORD flags;
30     source_elements_t *source;
31     parameter_t *parameters;
32     scope_chain_t *scope_chain;
33     parser_ctx_t *parser;
34     DWORD length;
35 } FunctionInstance;
36
37 static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0};
38
39 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
40 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
41 static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0};
42 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
43 static const WCHAR applyW[] = {'a','p','p','l','y',0};
44 static const WCHAR callW[] = {'c','a','l','l',0};
45 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
46 static const WCHAR propertyIsEnumerableW[] = {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
47 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
48
49 static IDispatch *get_this(DISPPARAMS *dp)
50 {
51     DWORD i;
52
53     for(i=0; i < dp->cNamedArgs; i++) {
54         if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
55             if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
56                 return V_DISPATCH(dp->rgvarg+i);
57
58             WARN("This is not VT_DISPATCH\n");
59             return NULL;
60         }
61     }
62
63     TRACE("no this passed\n");
64     return NULL;
65 }
66
67 static HRESULT create_var_disp(FunctionInstance *function, LCID lcid, DISPPARAMS *dp, jsexcept_t *ei,
68                                IServiceProvider *caller, DispatchEx **ret)
69 {
70     DispatchEx *var_disp;
71     HRESULT hres;
72
73     hres = create_dispex(function->dispex.ctx, NULL, NULL, &var_disp);
74     if(FAILED(hres))
75         return hres;
76
77     *ret = var_disp;
78     return S_OK;
79 }
80
81 static HRESULT invoke_source(FunctionInstance *function, IDispatch *this_obj, LCID lcid, DISPPARAMS *dp,
82         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
83 {
84     DispatchEx *var_disp;
85     exec_ctx_t *exec_ctx;
86     scope_chain_t *scope;
87     HRESULT hres;
88
89     if(!function->source) {
90         FIXME("no source\n");
91         return E_FAIL;
92     }
93
94     hres = create_var_disp(function, lcid, dp, ei, caller, &var_disp);
95     if(FAILED(hres))
96         return hres;
97
98     hres = scope_push(function->scope_chain, var_disp, &scope);
99     if(SUCCEEDED(hres)) {
100         hres = create_exec_ctx(this_obj, var_disp, scope, &exec_ctx);
101         scope_release(scope);
102     }
103     if(FAILED(hres))
104         return hres;
105
106     hres = exec_source(exec_ctx, function->parser, function->source, ei, retv);
107     exec_release(exec_ctx);
108
109     return hres;
110 }
111
112 static HRESULT invoke_function(FunctionInstance *function, LCID lcid, DISPPARAMS *dp,
113         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
114 {
115     IDispatch *this_obj;
116
117     if(!(this_obj = get_this(dp)))
118         this_obj = (IDispatch*)_IDispatchEx_(function->dispex.ctx->script_disp);
119
120     return invoke_source(function, this_obj, lcid, dp, retv, ei, caller);
121 }
122
123 static HRESULT invoke_value_proc(FunctionInstance *function, LCID lcid, WORD flags, DISPPARAMS *dp,
124         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
125 {
126     DispatchEx *this_obj = NULL;
127     IDispatch *this_disp;
128     HRESULT hres;
129
130     this_disp = get_this(dp);
131     if(this_disp)
132         this_obj = iface_to_jsdisp((IUnknown*)this_disp);
133
134     hres = function->value_proc(this_obj ? this_obj : function->dispex.ctx->script_disp, lcid,
135                                 flags, dp, retv, ei, caller);
136
137     if(this_obj)
138         jsdisp_release(this_obj);
139     return hres;
140 }
141
142 static HRESULT Function_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
143         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
144 {
145     FunctionInstance *This = (FunctionInstance*)dispex;
146
147     TRACE("%p %d\n", This, This->length);
148
149     switch(flags) {
150     case DISPATCH_PROPERTYGET:
151         V_VT(retv) = VT_I4;
152         V_I4(retv) = This->length;
153         break;
154     default:
155         FIXME("unimplemented flags %x\n", flags);
156         return E_NOTIMPL;
157     }
158
159     return S_OK;
160 }
161
162 static HRESULT Function_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
163         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
164 {
165     FIXME("\n");
166     return E_NOTIMPL;
167 }
168
169 static HRESULT Function_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
170         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
171 {
172     FIXME("\n");
173     return E_NOTIMPL;
174 }
175
176 static HRESULT Function_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
177         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
178 {
179     FIXME("\n");
180     return E_NOTIMPL;
181 }
182
183 static HRESULT Function_apply(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
184         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
185 {
186     FIXME("\n");
187     return E_NOTIMPL;
188 }
189
190 static HRESULT Function_call(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
191         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
192 {
193     FIXME("\n");
194     return E_NOTIMPL;
195 }
196
197 static HRESULT Function_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
198         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
199 {
200     FIXME("\n");
201     return E_NOTIMPL;
202 }
203
204 static HRESULT Function_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
205         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
206 {
207     FIXME("\n");
208     return E_NOTIMPL;
209 }
210
211 static HRESULT Function_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
212         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
213 {
214     FIXME("\n");
215     return E_NOTIMPL;
216 }
217
218 static HRESULT Function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
219         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
220 {
221     FunctionInstance *function;
222
223     TRACE("\n");
224
225     if(dispex->builtin_info->class != JSCLASS_FUNCTION) {
226         ERR("dispex is not a function\n");
227         return E_FAIL;
228     }
229
230     function = (FunctionInstance*)dispex;
231
232     switch(flags) {
233     case DISPATCH_METHOD:
234         if(function->value_proc)
235             return invoke_value_proc(function, lcid, flags, dp, retv, ei, caller);
236
237         return invoke_function(function, lcid, dp, retv, ei, caller);
238
239     default:
240         FIXME("not implemented flags %x\n", flags);
241         return E_NOTIMPL;
242     }
243
244     return S_OK;
245 }
246
247 static void Function_destructor(DispatchEx *dispex)
248 {
249     FunctionInstance *This = (FunctionInstance*)dispex;
250
251     if(This->parser)
252         parser_release(This->parser);
253     if(This->scope_chain)
254         scope_release(This->scope_chain);
255     heap_free(This);
256 }
257
258 static const builtin_prop_t Function_props[] = {
259     {applyW,                 Function_apply,                 PROPF_METHOD},
260     {callW,                  Function_call,                  PROPF_METHOD},
261     {hasOwnPropertyW,        Function_hasOwnProperty,        PROPF_METHOD},
262     {isPrototypeOfW,         Function_isPrototypeOf,         PROPF_METHOD},
263     {lengthW,                Function_length,                0},
264     {propertyIsEnumerableW,  Function_propertyIsEnumerable,  PROPF_METHOD},
265     {toLocaleStringW,        Function_toLocaleString,        PROPF_METHOD},
266     {toStringW,              Function_toString,              PROPF_METHOD},
267     {valueOfW,               Function_valueOf,               PROPF_METHOD}
268 };
269
270 static const builtin_info_t Function_info = {
271     JSCLASS_FUNCTION,
272     {NULL, Function_value, 0},
273     sizeof(Function_props)/sizeof(*Function_props),
274     Function_props,
275     Function_destructor,
276     NULL
277 };
278
279 static HRESULT create_function(script_ctx_t *ctx, DWORD flags, DispatchEx *prototype, FunctionInstance **ret)
280 {
281     FunctionInstance *function;
282     HRESULT hres;
283
284     function = heap_alloc_zero(sizeof(FunctionInstance));
285     if(!function)
286         return E_OUTOFMEMORY;
287
288     hres = init_dispex(&function->dispex, ctx, &Function_info, NULL);
289     if(FAILED(hres))
290         return hres;
291
292     function->flags = flags;
293     function->length = flags & PROPF_ARGMASK;
294
295     if(prototype) {
296         jsexcept_t jsexcept;
297         VARIANT var;
298
299         V_VT(&var) = VT_DISPATCH;
300         V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(prototype);
301         memset(&jsexcept, 0, sizeof(jsexcept));
302
303         hres = jsdisp_propput_name(&function->dispex, prototypeW, ctx->lcid, &var, &jsexcept, NULL/*FIXME*/);
304         if(FAILED(hres)) {
305             IDispatchEx_Release(_IDispatchEx_(&function->dispex));
306             return hres;
307         }
308     }
309
310     *ret = function;
311     return S_OK;
312 }
313
314 HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, DWORD flags,
315         DispatchEx *prototype, DispatchEx **ret)
316 {
317     FunctionInstance *function;
318     HRESULT hres;
319
320     hres = create_function(ctx, flags, prototype, &function);
321     if(FAILED(hres))
322         return hres;
323
324     function->value_proc = value_proc;
325
326     *ret = &function->dispex;
327     return S_OK;
328 }
329
330 HRESULT create_source_function(parser_ctx_t *ctx, parameter_t *parameters, source_elements_t *source,
331         scope_chain_t *scope_chain, DispatchEx **ret)
332 {
333     FunctionInstance *function;
334     parameter_t *iter;
335     DWORD length = 0;
336     HRESULT hres;
337
338     hres = create_function(ctx->script, PROPF_CONSTR, NULL, &function);
339     if(FAILED(hres))
340         return hres;
341
342     function->source = source;
343     function->parameters = parameters;
344
345     if(scope_chain) {
346         scope_addref(scope_chain);
347         function->scope_chain = scope_chain;
348     }
349
350     parser_addref(ctx);
351     function->parser = ctx;
352
353     for(iter = parameters; iter; iter = iter->next)
354         length++;
355     function->length = length;
356
357     *ret = &function->dispex;
358     return S_OK;
359 }