xref: /reactos/dll/win32/jscript/enumerator.c (revision 3e2d6582)
1 /*
2  * Copyright 2019 Andreas Maier
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 
23 #include "wine/debug.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
26 
27 typedef struct {
28     jsdisp_t dispex;
29     /* IEnumVARIANT returned by _NewEnum */
30     IEnumVARIANT *enumvar;
31     /* current item */
32     jsval_t item;
33     BOOL atend;
34 } EnumeratorInstance;
35 
36 static const WCHAR atEndW[] = {'a','t','E','n','d',0};
37 static const WCHAR itemW[] = {'i','t','e','m',0};
38 static const WCHAR moveFirstW[] = {'m','o','v','e','F','i','r','s','t',0};
39 static const WCHAR moveNextW[] = {'m','o','v','e','N','e','x','t',0};
40 
enumerator_from_jsdisp(jsdisp_t * jsdisp)41 static inline EnumeratorInstance *enumerator_from_jsdisp(jsdisp_t *jsdisp)
42 {
43     return CONTAINING_RECORD(jsdisp, EnumeratorInstance, dispex);
44 }
45 
enumerator_from_vdisp(vdisp_t * vdisp)46 static inline EnumeratorInstance *enumerator_from_vdisp(vdisp_t *vdisp)
47 {
48     return enumerator_from_jsdisp(vdisp->u.jsdisp);
49 }
50 
enumerator_this(vdisp_t * jsthis)51 static inline EnumeratorInstance *enumerator_this(vdisp_t *jsthis)
52 {
53     return is_vclass(jsthis, JSCLASS_ENUMERATOR) ? enumerator_from_vdisp(jsthis) : NULL;
54 }
55 
enumvar_get_next_item(EnumeratorInstance * This)56 static inline HRESULT enumvar_get_next_item(EnumeratorInstance *This)
57 {
58     HRESULT hres;
59     VARIANT nextitem;
60 
61     if (This->atend)
62         return S_OK;
63 
64     /* don't leak previous value */
65     jsval_release(This->item);
66 
67     /* not at end ... get next item */
68     VariantInit(&nextitem);
69     hres = IEnumVARIANT_Next(This->enumvar, 1, &nextitem, NULL);
70     if (hres == S_OK)
71     {
72         hres = variant_to_jsval(&nextitem, &This->item);
73         VariantClear(&nextitem);
74         if (FAILED(hres))
75         {
76             WARN("failed to convert jsval to variant!\n");
77             This->item = jsval_undefined();
78             return hres;
79         }
80     }
81     else
82     {
83         This->item = jsval_undefined();
84         This->atend = TRUE;
85     }
86 
87     return S_OK;
88 }
89 
Enumerator_destructor(jsdisp_t * dispex)90 static void Enumerator_destructor(jsdisp_t *dispex)
91 {
92     EnumeratorInstance *This = enumerator_from_jsdisp(dispex);
93 
94     TRACE("\n");
95 
96     jsval_release(This->item);
97     heap_free(dispex);
98 }
99 
Enumerator_atEnd(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)100 static HRESULT Enumerator_atEnd(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
101         jsval_t *r)
102 {
103     EnumeratorInstance *This;
104 
105     if (!(This = enumerator_this(jsthis)))
106         return throw_type_error(ctx, JS_E_ENUMERATOR_EXPECTED, NULL);
107 
108     TRACE("%d\n", This->atend);
109 
110     if (r)
111         *r = jsval_bool(This->atend);
112     return S_OK;
113 }
114 
Enumerator_item(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)115 static HRESULT Enumerator_item(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
116         jsval_t *r)
117 {
118     EnumeratorInstance *This;
119 
120     TRACE("\n");
121 
122     if (!(This = enumerator_this(jsthis)))
123         return throw_type_error(ctx, JS_E_ENUMERATOR_EXPECTED, NULL);
124 
125     return r ? jsval_copy(This->item, r) : S_OK;
126 }
127 
Enumerator_moveFirst(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)128 static HRESULT Enumerator_moveFirst(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
129         jsval_t *r)
130 {
131     EnumeratorInstance *This;
132     HRESULT hres = S_OK;
133 
134     TRACE("\n");
135 
136     if (!(This = enumerator_this(jsthis)))
137         return throw_type_error(ctx, JS_E_ENUMERATOR_EXPECTED, NULL);
138 
139     if (This->enumvar)
140     {
141         hres = IEnumVARIANT_Reset(This->enumvar);
142         if (FAILED(hres))
143             return hres;
144 
145         This->atend = FALSE;
146         hres = enumvar_get_next_item(This);
147         if(FAILED(hres))
148             return hres;
149     }
150 
151     if (r)
152         *r = jsval_undefined();
153     return S_OK;
154 }
155 
Enumerator_moveNext(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)156 static HRESULT Enumerator_moveNext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
157         jsval_t *r)
158 {
159     EnumeratorInstance *This;
160     HRESULT hres = S_OK;
161 
162     TRACE("\n");
163 
164     if (!(This = enumerator_this(jsthis)))
165         return throw_type_error(ctx, JS_E_ENUMERATOR_EXPECTED, NULL);
166 
167     if (This->enumvar)
168     {
169         hres = enumvar_get_next_item(This);
170         if (FAILED(hres))
171             return hres;
172     }
173 
174     if (r)
175         *r = jsval_undefined();
176     return S_OK;
177 }
178 
179 static const builtin_prop_t Enumerator_props[] = {
180     {atEndW,     Enumerator_atEnd,     PROPF_METHOD},
181     {itemW,      Enumerator_item,      PROPF_METHOD},
182     {moveFirstW, Enumerator_moveFirst, PROPF_METHOD},
183     {moveNextW,  Enumerator_moveNext,  PROPF_METHOD},
184 };
185 
186 static const builtin_info_t Enumerator_info = {
187     JSCLASS_ENUMERATOR,
188     {NULL, NULL, 0},
189     ARRAY_SIZE(Enumerator_props),
190     Enumerator_props,
191     NULL,
192     NULL
193 };
194 
195 static const builtin_info_t EnumeratorInst_info = {
196     JSCLASS_ENUMERATOR,
197     {NULL, NULL, 0, NULL},
198     0,
199     NULL,
200     Enumerator_destructor,
201     NULL
202 };
203 
alloc_enumerator(script_ctx_t * ctx,jsdisp_t * object_prototype,EnumeratorInstance ** ret)204 static HRESULT alloc_enumerator(script_ctx_t *ctx, jsdisp_t *object_prototype, EnumeratorInstance **ret)
205 {
206     EnumeratorInstance *enumerator;
207     HRESULT hres;
208 
209     enumerator = heap_alloc_zero(sizeof(EnumeratorInstance));
210     if(!enumerator)
211         return E_OUTOFMEMORY;
212 
213     if(object_prototype)
214         hres = init_dispex(&enumerator->dispex, ctx, &Enumerator_info, object_prototype);
215     else
216         hres = init_dispex_from_constr(&enumerator->dispex, ctx, &EnumeratorInst_info,
217                                        ctx->enumerator_constr);
218 
219     if(FAILED(hres))
220     {
221         heap_free(enumerator);
222         return hres;
223     }
224 
225     *ret = enumerator;
226     return S_OK;
227 }
228 
create_enumerator(script_ctx_t * ctx,jsval_t * argv,jsdisp_t ** ret)229 static HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **ret)
230 {
231     EnumeratorInstance *enumerator;
232     HRESULT hres;
233     IDispatch *obj;
234     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
235     IEnumVARIANT *enumvar = NULL;
236 
237     if (argv)
238     {
239         VARIANT varresult;
240 
241         if (!is_object_instance(*argv))
242         {
243             FIXME("I don't know how to handle this type!\n");
244             return E_NOTIMPL;
245         }
246 
247         obj = get_object(*argv);
248 
249         /* Try to get a IEnumVARIANT by _NewEnum */
250         VariantInit(&varresult);
251         hres = IDispatch_Invoke(obj, DISPID_NEWENUM, &IID_NULL, LOCALE_NEUTRAL,
252                 DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
253         if (FAILED(hres))
254         {
255             WARN("Enumerator: no DISPID_NEWENUM.\n");
256             return E_INVALIDARG;
257         }
258 
259         if ((V_VT(&varresult) == VT_DISPATCH) || (V_VT(&varresult) == VT_UNKNOWN))
260         {
261             hres = IUnknown_QueryInterface(V_UNKNOWN(&varresult),
262                 &IID_IEnumVARIANT, (void**)&enumvar);
263         }
264         else
265         {
266             FIXME("Enumerator: NewEnum unexpected type of varresult (%d).\n", V_VT(&varresult));
267             hres = E_INVALIDARG;
268         }
269         VariantClear(&varresult);
270         if (FAILED(hres))
271             return hres;
272     }
273 
274     hres = alloc_enumerator(ctx, NULL, &enumerator);
275     if (FAILED(hres))
276     {
277         if (enumvar)
278             IEnumVARIANT_Release(enumvar);
279         return hres;
280     }
281 
282     enumerator->enumvar = enumvar;
283     enumerator->atend = !enumvar;
284     hres = enumvar_get_next_item(enumerator);
285     if (FAILED(hres))
286     {
287         jsdisp_release(&enumerator->dispex);
288         return hres;
289     }
290 
291     *ret = &enumerator->dispex;
292     return S_OK;
293 }
294 
EnumeratorConstr_value(script_ctx_t * ctx,vdisp_t * vthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)295 static HRESULT EnumeratorConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
296         jsval_t *r)
297 {
298     jsdisp_t *obj;
299     HRESULT hres;
300 
301     TRACE("\n");
302 
303     switch(flags) {
304     case DISPATCH_CONSTRUCT: {
305         if (argc > 1)
306             return throw_syntax_error(ctx, JS_E_INVALIDARG, NULL);
307 
308         hres = create_enumerator(ctx, (argc == 1) ? &argv[0] : 0, &obj);
309         if(FAILED(hres))
310             return hres;
311 
312         *r = jsval_obj(obj);
313         break;
314     }
315     default:
316         FIXME("unimplemented flags: %x\n", flags);
317         return E_NOTIMPL;
318     }
319 
320     return S_OK;
321 }
322 
323 static const builtin_info_t EnumeratorConstr_info = {
324     JSCLASS_FUNCTION,
325     DEFAULT_FUNCTION_VALUE,
326     0,
327     NULL,
328     NULL,
329     NULL
330 };
331 
create_enumerator_constr(script_ctx_t * ctx,jsdisp_t * object_prototype,jsdisp_t ** ret)332 HRESULT create_enumerator_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
333 {
334     EnumeratorInstance *enumerator;
335     HRESULT hres;
336     static const WCHAR EnumeratorW[] = {'E','n','u','m','e','r','a','t','o','r',0};
337 
338     hres = alloc_enumerator(ctx, object_prototype, &enumerator);
339     if(FAILED(hres))
340         return hres;
341 
342     hres = create_builtin_constructor(ctx, EnumeratorConstr_value,
343                                      EnumeratorW, &EnumeratorConstr_info,
344                                      PROPF_CONSTR|7, &enumerator->dispex, ret);
345     jsdisp_release(&enumerator->dispex);
346 
347     return hres;
348 }
349