/* * Copyright 2019 Andreas Maier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include #include #include "initguid.h" #include "jscript.h" #include "jscript_classes.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(jscript); #define DATATYPE_ARRAY 0 #define DATATYPE_STRINGLIST 1 typedef struct { jsdisp_t dispex; int index; int length; int datatype; // constructor with jsarray e.g. ["A","B"] jsdisp_t *array; // constructor with stringlist-interface // methods Items and Count are available. // e.g. StringList or IDriveCollection-Interface IDispatch *stringlist; } EnumeratorInstance; static const WCHAR atEndW[] = {'a','t','E','n','d',0}; static const WCHAR itemW[] = {'i','t','e','m',0}; static const WCHAR moveFirstW[] = {'m','o','v','e','F','i','r','s','t',0}; static const WCHAR moveNextW[] = {'m','o','v','e','N','e','x','t',0}; /* Stringlist_xxx-functions borrowed from wintest (msi) * FIXME: share it to avoid code duplication ... */ /* all these interface have the same item and count-property :-) */ DEFINE_GUID(IID_StringList, 0x000C1095, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); DEFINE_GUID(IID_IDriveCollection, 0xc7c3f5a1, 0x88a3, 0x11d0, 0xab, 0xcb, 0x00, 0xa0, 0xc9, 0x0f, 0xff, 0xc0); static EXCEPINFO excepinfo; static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult) { OLECHAR *name = NULL; DISPID dispid; HRESULT hr; UINT i; UINT len; memset(pVarResult, 0, sizeof(VARIANT)); VariantInit(pVarResult); len = MultiByteToWideChar(CP_ACP, 0, szName, -1, NULL, 0 ); name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR) ); if (!name) return E_FAIL; MultiByteToWideChar(CP_ACP, 0, szName, -1, name, len ); hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid); HeapFree(GetProcessHeap(), 0, name); if (FAILED(hr)) return hr; memset(&excepinfo, 0, sizeof(excepinfo)); hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_NEUTRAL, wFlags, pDispParams, pVarResult, &excepinfo, NULL); if ((hr == S_OK) && (vtResult != VT_EMPTY)) hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult); for (i=0; icArgs; i++) VariantClear(&pDispParams->rgvarg[i]); return hr; } static HRESULT StringList_Item(IDispatch *pStringList, int iIndex, LPWSTR szString) { VARIANT varresult; VARIANTARG vararg[1]; DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; HRESULT hr; VariantInit(&vararg[0]); V_VT(&vararg[0]) = VT_I4; V_I4(&vararg[0]) = iIndex; hr = invoke(pStringList, "Item", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_BSTR); if (V_BSTR(&varresult)) lstrcpyW(szString, V_BSTR(&varresult)); VariantClear(&varresult); return hr; } static HRESULT StringList_Count(IDispatch *pStringList, int *pCount) { VARIANT varresult; DISPPARAMS dispparams = {NULL, NULL, 0, 0}; HRESULT hr = invoke(pStringList, "Count", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_I4); *pCount = V_I4(&varresult); VariantClear(&varresult); return hr; } static inline EnumeratorInstance *enumerator_from_jsdisp(jsdisp_t *jsdisp) { return CONTAINING_RECORD(jsdisp, EnumeratorInstance, dispex); } static inline EnumeratorInstance *enumerator_from_vdisp(vdisp_t *vdisp) { return enumerator_from_jsdisp(vdisp->u.jsdisp); } static inline void enum_set_item_result(EnumeratorInstance *This, jsval_t *r) { HRESULT hres; LPWSTR item; if (!r) return; if ((This->index < 0) || (This->index >= This->length)) { *r = jsval_undefined(); return; } switch (This->datatype) { case DATATYPE_ARRAY: { hres = jsdisp_get_idx(This->array, This->index, r); if (FAILED(hres)) *r = jsval_undefined(); break; } case DATATYPE_STRINGLIST: { // FIXME: buffer size?? //DebugBreak(); item = (LPWSTR)malloc(100);//?? hres = StringList_Item(This->stringlist, This->index, item); if (FAILED(hres)) { *r = jsval_undefined(); } else { FIXME("item 0 %S\n", item); //FIXME: Addref??? *r = jsval_string(jsstr_alloc(item)); } free(item); break; } default: { *r = jsval_undefined(); } } } static void Enumerator_destructor(jsdisp_t *dispex) { EnumeratorInstance *This; This = enumerator_from_jsdisp(dispex); if (This->array) jsdisp_release(This->array); if (This->stringlist) IDispatch_Release(This->stringlist); FIXME("destructor\n"); heap_free(dispex); } HRESULT Enumerator_atEnd(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { EnumeratorInstance *This; This = enumerator_from_vdisp(jsthis); if (r) *r = jsval_bool(This->index >= This->length); FIXME("JSEnumerator_atEnd %i %i\n", This->length, This->index); return S_OK; } HRESULT Enumerator_item(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { EnumeratorInstance *This; This = enumerator_from_vdisp(jsthis); enum_set_item_result(This, r); FIXME("JSEnumerator_item\n"); return S_OK; } HRESULT Enumerator_moveFirst(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { EnumeratorInstance *This; This = enumerator_from_vdisp(jsthis); This->index = 0; enum_set_item_result(This, r); FIXME("JSEnumerator_moveFirst\n"); return S_OK; } HRESULT Enumerator_moveNext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { EnumeratorInstance *This; This = enumerator_from_vdisp(jsthis); if (This->index < This->length) This->index++; enum_set_item_result(This, r); FIXME("JSEnumerator_moveNext\n"); return S_OK; } static const builtin_prop_t Enumerator_props[] = { {atEndW, Enumerator_atEnd, PROPF_METHOD}, {itemW, Enumerator_item, PROPF_METHOD}, {moveFirstW, Enumerator_moveFirst, PROPF_METHOD}, {moveNextW, Enumerator_moveNext, PROPF_METHOD}, }; static const builtin_info_t Enumerator_info = { JSCLASS_OBJECT/*?JSCLASS_ENUMERATOR?*/, {NULL, NULL, 0}, ARRAY_SIZE(Enumerator_props), Enumerator_props, NULL, NULL }; static const builtin_info_t EnumeratorInst_info = { JSCLASS_OBJECT, {NULL, NULL, 0, NULL},//{NULL, NULL,0, Array_get_value}, 0,//ARRAY_SIZE(EnumeratorInst_props), NULL,//EnumeratorInst_props, Enumerator_destructor, NULL//Enumerator_on_put }; static HRESULT EnumeratorConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { jsdisp_t *obj; /*DWORD i;*/ HRESULT hres; jsstr_t *str; const WCHAR *buf; TRACE("EnumeratorConstr_value\n"); hres = to_flat_string(ctx, argv[0], &str, &buf); if(FAILED(hres)) buf = L"???"; FIXME("Enum %d %S \n", argc, (WCHAR*)buf); jsstr_release(str); switch(flags) { /*case DISPATCH_METHOD:*/ case DISPATCH_CONSTRUCT: { if (argc != 1) return throw_syntax_error(ctx, JS_E_MISSING_ARG, NULL); FIXME("dispatch\n"); /*if(argc == 1 && is_number(argv[0])) { double n = get_number(argv[0]); if(n < 0 || !is_int32(n)) return throw_range_error(ctx, JS_E_INVALID_LENGTH, NULL); hres = create_array(ctx, n, &obj); if(FAILED(hres)) return hres; *r = jsval_obj(obj); return S_OK; }*/ //hres = create_array(ctx, argc, &obj); hres = create_enumerator(ctx, &argv[0], &obj); if(FAILED(hres)) return hres; /*for(i=0; i < argc; i++) { hres = jsdisp_propput_idx(obj, i, argv[i]); if(FAILED(hres)) break; } if(FAILED(hres)) { jsdisp_release(obj); return hres; }*/ *r = jsval_obj(obj); break; } default: FIXME("unimplemented flags: %x\n", flags); return E_NOTIMPL; } return S_OK; //create_enumerator_constr(ctx, ) //hres = create_dispex(ctx, &JSEnumerator_class, NULL, &ctx->global); //if(FAILED(hres)) // return hres; } static HRESULT alloc_enumerator(script_ctx_t *ctx, jsdisp_t *object_prototype, EnumeratorInstance **ret) { EnumeratorInstance *enumerator; HRESULT hres; enumerator = heap_alloc_zero(sizeof(EnumeratorInstance)); if(!enumerator) return E_OUTOFMEMORY; if(object_prototype) hres = init_dispex(&enumerator->dispex, ctx, &Enumerator_info, object_prototype); else hres = init_dispex_from_constr(&enumerator->dispex, ctx, &EnumeratorInst_info, ctx->enumerator_constr); if(FAILED(hres)) { heap_free(enumerator); return hres; } *ret = enumerator; return S_OK; } /*static const WCHAR isArrayW[] = {'i','s','A','r','r','a','y',0}; */ //static const builtin_prop_t EnumeratorConstr_props[] = { //{isArrayW, ArrayConstr_isArray, PROPF_ES5|PROPF_METHOD|1} //}; static const builtin_info_t EnumeratorConstr_info = { JSCLASS_FUNCTION, DEFAULT_FUNCTION_VALUE, 0,//ARRAY_SIZE(EnumeratorConstr_props), NULL,//EnumeratorConstr_props, NULL, NULL }; HRESULT create_enumerator_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret) { EnumeratorInstance *enumerator; HRESULT hres; static const WCHAR EnumeratorW[] = {'E','n','u','m','e','r','a','t','o','r',0}; hres = alloc_enumerator(ctx, object_prototype, &enumerator); if(FAILED(hres)) return hres; hres = create_builtin_constructor(ctx, EnumeratorConstr_value, EnumeratorW, &EnumeratorConstr_info, PROPF_CONSTR|1, &enumerator->dispex, ret); jsdisp_release(&enumerator->dispex); return hres; } void dump_type_info(IDispatch *spDisp) { BSTR funcName; HRESULT hr; ITypeInfo* spTypeInfo; hr = IDispatch_GetTypeInfo(spDisp, 0,0, &spTypeInfo); if(SUCCEEDED(hr) && spTypeInfo) { TYPEATTR *pTatt = NULL; hr = ITypeInfo_GetTypeAttr(spTypeInfo, &pTatt); if(SUCCEEDED(hr) && pTatt) { FUNCDESC * fd = NULL; int i; FIXME("GUID %s\n", debugstr_guid(&pTatt->guid)); funcName = NULL; ITypeInfo_GetDocumentation( spTypeInfo, MEMBERID_NIL, &funcName, NULL, NULL, NULL); FIXME("%S\n", funcName); SysFreeString(funcName); for(i = 0; i < pTatt->cFuncs; ++i) { hr = ITypeInfo_GetFuncDesc(spTypeInfo, i, &fd); if(SUCCEEDED(hr) && fd) { //CComBSTR funcName; funcName = NULL;//SysAllocStringByteLen(NULL, 1024); //x< init BSTR?? //spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr); ITypeInfo_GetDocumentation(spTypeInfo, fd->memid, &funcName, NULL, NULL, NULL); if (SysStringLen(funcName) > 0) { //methodsMap[fd->memid] = funcName; FIXME("%S\n", funcName); } SysFreeString(funcName); //spTypeInfo->ReleaseFuncDesc(fd); ITypeInfo_ReleaseFuncDesc(spTypeInfo, fd); } } //spTypeInfo->ReleaseTypeAttr(pTatt); ITypeInfo_ReleaseTypeAttr(spTypeInfo, pTatt); } } } HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **ret) { EnumeratorInstance *enumerator; HRESULT hres; IDispatch *dummy; IDispatch *obj = NULL; int jsdatatype = -1; int jslength = 0; jsdisp_t *jsarray = NULL; IDispatch *jsstringlist = NULL; if(is_object_instance(*argv)) //if (__JSVAL_OBJ(*argv)) { obj = get_object(*argv); jsarray = iface_to_jsdisp(obj);//AddReference(obj) if ((jsarray) && (is_class(jsarray, JSCLASS_ARRAY))) { jsdatatype = DATATYPE_ARRAY; jslength = array_get_length(jsarray); } else { if (jsarray) jsdisp_release(jsarray); //FIXME("Obj != array not implemented!\n"); //DebugBreak(); /* check if its a stringlist or drivecollection */ hres = IDispatch_QueryInterface(obj, &IID_StringList, (void*)&dummy); if (FAILED(hres)) hres = IDispatch_QueryInterface(obj, &IID_IDriveCollection, (void*)&dummy); if (FAILED(hres)) { FIXME("Unexpected type!\n"); dump_type_info(obj); return E_INVALIDARG; } FIXME("ok\n"); hres = StringList_Count(obj, &jslength); if (FAILED(hres)) { ERR("StringList_Count failed %d\n", hres); return -1; } FIXME("StringList_Count %i\n", jslength); // hack ... will be removed if code // for interfaces (above) works #if 0 { /* create a array */ jsstr_t *jsstr; jsstr = jsstr_alloc(L"works");//Free?? //HACK jslength = 3; create_array(ctx, 3, &jsarray); jsdisp_propput_idx(jsarray, 0, jsval_string(jsstr)); jsdisp_propput_idx(jsarray, 1, jsval_number(6)); jsdisp_propput_idx(jsarray, 2, jsval_number(8)); jsdatatype = DATATYPE_ARRAY; jsstringlist = NULL; } FIXME("HACK\n"); //return E_NOTIMPL;*/ #else jsdatatype = DATATYPE_STRINGLIST; jsstringlist = obj; #endif } } else { FIXME("Type not implemented!\n"); return E_NOTIMPL; } //DebugBreak(); hres = alloc_enumerator(ctx, NULL, &enumerator); if(FAILED(hres)) return hres; //FIXME: should i add referencens ... array / stringlist? //jsdisp_addref(jsarray); if (jsstringlist) IDispatch_AddRef(jsstringlist); enumerator->index = 0; enumerator->length = jslength; enumerator->datatype = jsdatatype; enumerator->array = jsarray; enumerator->stringlist = jsstringlist; *ret = &enumerator->dispex; return S_OK; }