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 41 static inline EnumeratorInstance *enumerator_from_jsdisp(jsdisp_t *jsdisp) 42 { 43 return CONTAINING_RECORD(jsdisp, EnumeratorInstance, dispex); 44 } 45 46 static inline EnumeratorInstance *enumerator_from_vdisp(vdisp_t *vdisp) 47 { 48 return enumerator_from_jsdisp(vdisp->u.jsdisp); 49 } 50 51 static inline EnumeratorInstance *enumerator_this(vdisp_t *jsthis) 52 { 53 return is_vclass(jsthis, JSCLASS_ENUMERATOR) ? enumerator_from_vdisp(jsthis) : NULL; 54 } 55 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 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 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 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 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 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 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 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 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 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