[PATCH 1/2] [PATCH 1/2] jscript: (partly) implement Enumerator object

Andreas Maier staubim at quantentunnel.de
Mon Apr 15 14:26:41 CDT 2019


Enumerator is an MS extension.
This fixes installatiion of VyChat219.msi.

Signed-off-by: Andreas Maier <staubim at quantentunnel.de>
---
 dlls/jscript/Makefile.in   |   1 +
 dlls/jscript/enumerator.c  | 455 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/jscript/global.c      |  18 +-
 dlls/jscript/jscript.h     |  10 +
 dlls/jscript/jsglobal.idl  |   3 -
 dlls/jscript/tests/rsrc.rc |   3 +
 dlls/jscript/tests/run.c   |   1 +
 7 files changed, 480 insertions(+), 11 deletions(-)
 create mode 100644 dlls/jscript/enumerator.c

diff --git a/dlls/jscript/Makefile.in b/dlls/jscript/Makefile.in
index 34ed193742..1cc58a5ed6 100644
--- a/dlls/jscript/Makefile.in
+++ b/dlls/jscript/Makefile.in
@@ -10,6 +10,7 @@ C_SRCS = \
 	decode.c \
 	dispex.c \
 	engine.c \
+	enumerator.c \
 	error.c \
 	function.c \
 	global.c \
diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c
new file mode 100644
index 0000000000..bd05cdf4da
--- /dev/null
+++ b/dlls/jscript/enumerator.c
@@ -0,0 +1,455 @@
+/*
+ * 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 <math.h>
+#include <assert.h>
+
+#include "jscript.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(jscript);
+
+#define DATATYPE_ARRAY           0
+#define DATATYPE_STRINGLIST      1
+#define DATATYPE_DRIVECOLLECTION 2
+
+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};
+
+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; i<pDispParams->cArgs; i++)
+        VariantClear(&pDispParams->rgvarg[i]);
+
+    return hr;
+}
+
+static HRESULT Dispatch_ItemAllocStr(IDispatch *pStringList, int iIndex, LPWSTR *szString)
+{
+    VARIANT varresult;
+    VARIANTARG vararg[1];
+    DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0};
+    UINT charCount;
+    HRESULT hr;
+
+    *szString = NULL;
+
+    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 (FAILED(hr))
+        goto CLEANUP;
+
+    charCount = SysStringLen(V_BSTR(&varresult)) + 1;
+    *szString = (LPWSTR)malloc(charCount * sizeof(WCHAR));
+    if (!(*szString))
+    {
+        hr = ERROR_OUTOFMEMORY;
+        goto CLEANUP;
+    }
+    if (!lstrcpynW(*szString, V_BSTR(&varresult), charCount))
+    {
+        hr = E_FAIL;
+        goto CLEANUP;
+    }
+CLEANUP:
+    VariantClear(&varresult);
+    if (FAILED(hr))
+    {
+        free(*szString);
+        *szString = NULL;
+    }
+    return hr;
+}
+
+static HRESULT Dispatch_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;
+    WCHAR* 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:
+        {
+            hres = Dispatch_ItemAllocStr(This->stringlist, This->index, &item);
+            if (FAILED(hres))
+            {
+                TRACE("item %d <failed>\n", This->index);
+                *r = jsval_undefined();
+            } else
+            {
+                TRACE("item %d %s\n", This->index, debugstr_w(item));
+                *r = jsval_string(jsstr_alloc(item));
+                free(item);
+            }
+            break;
+        }
+        case DATATYPE_DRIVECOLLECTION:
+        {
+            FIXME("getitem for drivecollection not implemented!\n");
+            *r = jsval_undefined();
+            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);
+
+    TRACE("Enumerator_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);
+
+    TRACE("Enumerator_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;
+
+    TRACE("Enumerator_item\n");
+
+    This = enumerator_from_vdisp(jsthis);
+
+    enum_set_item_result(This, r);
+
+    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;
+
+    TRACE("Enumerator_moveFirst\n");
+
+    This = enumerator_from_vdisp(jsthis);
+    This->index = 0;
+
+    enum_set_item_result(This, r);
+
+    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);
+
+    TRACE("Enumerator_moveNext\n");
+
+    if (This->index < This->length)
+        This->index++;
+
+    enum_set_item_result(This, r);
+
+
+    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,
+    {NULL, NULL, 0},
+    ARRAY_SIZE(Enumerator_props),
+    Enumerator_props,
+    NULL,
+    NULL
+};
+
+static const builtin_info_t EnumeratorInst_info = {
+    JSCLASS_OBJECT,
+    {NULL, NULL, 0, NULL},
+    0,
+    NULL,
+    Enumerator_destructor,
+    NULL
+};
+
+static HRESULT EnumeratorConstr_value(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv,
+        jsval_t *r)
+{
+    jsdisp_t *obj;
+    HRESULT hres;
+
+    TRACE("EnumeratorConstr_value\n");
+
+    switch(flags) {
+    case DISPATCH_CONSTRUCT: {
+        if (argc != 1)
+            return throw_syntax_error(ctx, JS_E_MISSING_ARG, NULL);
+
+        hres = create_enumerator(ctx, &argv[0], &obj);
+        if(FAILED(hres))
+            return hres;
+
+        *r = jsval_obj(obj);
+        break;
+    }
+    default:
+        FIXME("unimplemented flags: %x\n", flags);
+        return E_NOTIMPL;
+    }
+
+    return S_OK;
+}
+
+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 builtin_info_t EnumeratorConstr_info = {
+    JSCLASS_FUNCTION,
+    DEFAULT_FUNCTION_VALUE,
+    0,
+    NULL,
+    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;
+}
+
+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))
+    {
+        obj = get_object(*argv);
+        jsarray = iface_to_jsdisp(obj);
+        if ((jsarray) &&
+            (is_class(jsarray, JSCLASS_ARRAY)))
+        {
+            TRACE("array\n");
+            jsdatatype = DATATYPE_ARRAY;
+            jslength = array_get_length(jsarray);
+        }
+        else
+        {
+            if (jsarray)
+                jsdisp_release(jsarray);
+            /* check if its a stringlist or drivecollection */
+            if (SUCCEEDED(IDispatch_QueryInterface(obj, &IID_StringList, (void*)&dummy)))
+            {
+                TRACE("Enumerator: stringlist\n");
+                jsdatatype = DATATYPE_STRINGLIST;
+            }
+            else if (SUCCEEDED(IDispatch_QueryInterface(obj, &IID_IDriveCollection, (void*)&dummy)))
+            {
+                TRACE("Enumerator: drivecollection\n");
+                jsdatatype = DATATYPE_DRIVECOLLECTION;
+            }
+            else
+            {
+                FIXME("Enumerator: unexpected type!\n");
+                return E_INVALIDARG;
+            }
+
+            hres = Dispatch_Count(obj, &jslength);
+            if (FAILED(hres))
+            {
+                ERR("Dispatch_Count failed %d\n", hres);
+                return -1;
+            }
+            TRACE("Count %i\n", jslength);
+
+            jsstringlist = obj;
+        }
+    }
+    else
+    {
+        FIXME("type not implemented!\n");
+        return E_NOTIMPL;
+    }
+
+    hres = alloc_enumerator(ctx, NULL, &enumerator);
+    if(FAILED(hres))
+        return hres;
+
+    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;
+}
diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c
index 33de641483..61a756be0e 100644
--- a/dlls/jscript/global.c
+++ b/dlls/jscript/global.c
@@ -113,13 +113,6 @@ static WCHAR int_to_char(int i)
     return 'A'+i-10;
 }

-static HRESULT JSGlobal_Enumerator(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
-        jsval_t *r)
-{
-    FIXME("\n");
-    return E_NOTIMPL;
-}
-
 static HRESULT JSGlobal_escape(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
         jsval_t *r)
 {
@@ -942,7 +935,6 @@ static HRESULT JSGlobal_decodeURIComponent(script_ctx_t *ctx, vdisp_t *jsthis, W

 static const builtin_prop_t JSGlobal_props[] = {
     {CollectGarbageW,            JSGlobal_CollectGarbage,            PROPF_METHOD},
-    {EnumeratorW,                JSGlobal_Enumerator,                PROPF_METHOD|7},
     {_GetObjectW,                JSGlobal_GetObject,                 PROPF_METHOD|2},
     {ScriptEngineW,              JSGlobal_ScriptEngine,              PROPF_METHOD},
     {ScriptEngineBuildVersionW,  JSGlobal_ScriptEngineBuildVersion,  PROPF_METHOD},
@@ -1019,6 +1011,16 @@ static HRESULT init_constructors(script_ctx_t *ctx, jsdisp_t *object_prototype)
     if(FAILED(hres))
         return hres;

+    hres = create_enumerator_constr(ctx, object_prototype, &ctx->enumerator_constr);
+    if(FAILED(hres))
+        return hres;
+
+    /* Enumerator is a MS extension */
+    hres = jsdisp_define_data_property(ctx->global, EnumeratorW, PROPF_WRITABLE,
+                                       jsval_obj(ctx->enumerator_constr));
+    if(FAILED(hres))
+        return hres;
+
     hres = init_error_constr(ctx, object_prototype);
     if(FAILED(hres))
         return hres;
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h
index 2af5b598bd..de539b77b1 100644
--- a/dlls/jscript/jscript.h
+++ b/dlls/jscript/jscript.h
@@ -326,6 +326,7 @@ HRESULT create_bool(script_ctx_t*,BOOL,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_number(script_ctx_t*,double,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_vbarray(script_ctx_t*,SAFEARRAY*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_json(script_ctx_t*,jsdisp_t**) DECLSPEC_HIDDEN;
+HRESULT create_enumerator(script_ctx_t *ctx, jsval_t *argv, jsdisp_t **ret) DECLSPEC_HIDDEN;

 typedef enum {
     NO_HINT,
@@ -436,6 +437,7 @@ struct _script_ctx_t {
     jsdisp_t *array_constr;
     jsdisp_t *bool_constr;
     jsdisp_t *date_constr;
+    jsdisp_t *enumerator_constr;
     jsdisp_t *error_constr;
     jsdisp_t *eval_error_constr;
     jsdisp_t *range_error_constr;
@@ -468,6 +470,7 @@ HRESULT create_array_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_bool_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_date_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT init_error_constr(script_ctx_t*,jsdisp_t*) DECLSPEC_HIDDEN;
+HRESULT create_enumerator_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_number_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_object_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
 HRESULT create_regexp_constr(script_ctx_t*,jsdisp_t*,jsdisp_t**) DECLSPEC_HIDDEN;
@@ -590,3 +593,10 @@ static inline void unlock_module(void)
 {
     InterlockedDecrement(&module_ref);
 }
+
+/* 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);
diff --git a/dlls/jscript/jsglobal.idl b/dlls/jscript/jsglobal.idl
index b8604e99f2..7e0c1a30cf 100644
--- a/dlls/jscript/jsglobal.idl
+++ b/dlls/jscript/jsglobal.idl
@@ -74,9 +74,6 @@ library JSGlobal
         [id(DISPID_GLOBAL_VBARRAY)]
         VARIANT VBArray();

-        [id(DISPID_GLOBAL_ENUMERATOR)]
-        VARIANT Enumerator();
-
         [id(DISPID_GLOBAL_ESCAPE)]
         VARIANT escape(VARIANT String);

diff --git a/dlls/jscript/tests/rsrc.rc b/dlls/jscript/tests/rsrc.rc
index d9ee6b23ae..b130df294a 100644
--- a/dlls/jscript/tests/rsrc.rc
+++ b/dlls/jscript/tests/rsrc.rc
@@ -25,6 +25,9 @@ cc.js 40 "cc.js"
 /* @makedep: lang.js */
 lang.js 40 "lang.js"

+/* @makedep: extension.js */
+extension.js 40 "extension.js"
+
 /* @makedep: regexp.js */
 regexp.js 40 "regexp.js"

diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c
index aadb31700d..9e92a418f1 100644
--- a/dlls/jscript/tests/run.c
+++ b/dlls/jscript/tests/run.c
@@ -2684,6 +2684,7 @@ static BOOL run_tests(void)

     run_from_res("lang.js");
     run_from_res("api.js");
+    run_from_res("extension.js");
     run_from_res("regexp.js");
     run_from_res("cc.js");

--
2.11.0




More information about the wine-devel mailing list