[PATCH 6/9] msscript.ocx: Cache the procedures obtained from the ScriptProcedureCollection.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Aug 10 09:58:01 CDT 2020


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

Since we're computing the hash anyway for ITypeComp::Bind, a simple fixed hash
table has been used to cache them much more efficiently than a linear lookup.

 dlls/msscript.ocx/msscript.c       | 59 ++++++++++++++++++++++++++++--
 dlls/msscript.ocx/tests/msscript.c | 12 +++++-
 2 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/dlls/msscript.ocx/msscript.c b/dlls/msscript.ocx/msscript.c
index df173be..ccdbba4 100644
--- a/dlls/msscript.ocx/msscript.c
+++ b/dlls/msscript.ocx/msscript.c
@@ -93,6 +93,9 @@ typedef struct {
     IScriptProcedure IScriptProcedure_iface;
     LONG ref;
 
+    ULONG hash;
+    struct list entry;
+
     BSTR name;
 } ScriptProcedure;
 
@@ -102,6 +105,7 @@ struct ScriptProcedureCollection {
 
     LONG count;
     ScriptModule *module;
+    struct list hash_table[43];
 };
 
 struct ScriptHost {
@@ -800,6 +804,7 @@ static ULONG WINAPI ScriptProcedure_Release(IScriptProcedure *iface)
 
     if (!ref)
     {
+        list_remove(&This->entry);
         SysFreeString(This->name);
         heap_free(This);
     }
@@ -908,9 +913,12 @@ static const IScriptProcedureVtbl ScriptProcedureVtbl = {
 };
 
 /* This function always releases the FUNCDESC passed in */
-static HRESULT get_script_procedure(ITypeInfo *typeinfo, FUNCDESC *desc, IScriptProcedure **procedure)
+static HRESULT get_script_procedure(ScriptProcedureCollection *procedures, ITypeInfo *typeinfo,
+        FUNCDESC *desc, IScriptProcedure **procedure)
 {
+    struct list *proc_list;
     ScriptProcedure *proc;
+    ULONG hash;
     HRESULT hr;
     BSTR str;
     UINT len;
@@ -918,6 +926,23 @@ static HRESULT get_script_procedure(ITypeInfo *typeinfo, FUNCDESC *desc, IScript
     hr = ITypeInfo_GetNames(typeinfo, desc->memid, &str, 1, &len);
     if (FAILED(hr)) goto done;
 
+    len = SysStringLen(str);
+    hash = LHashValOfNameSys(sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32, LOCALE_USER_DEFAULT, str);
+    proc_list = &procedures->hash_table[hash % ARRAY_SIZE(procedures->hash_table)];
+
+    /* Try to find it in the hash table */
+    LIST_FOR_EACH_ENTRY(proc, proc_list, ScriptProcedure, entry)
+    {
+        if (proc->hash == hash && SysStringLen(proc->name) == len &&
+            !memcmp(proc->name, str, len * sizeof(*str)))
+        {
+            SysFreeString(str);
+            IScriptProcedure_AddRef(&proc->IScriptProcedure_iface);
+            *procedure = &proc->IScriptProcedure_iface;
+            goto done;
+        }
+    }
+
     if (!(proc = heap_alloc(sizeof(*proc))))
     {
         hr = E_OUTOFMEMORY;
@@ -927,7 +952,9 @@ static HRESULT get_script_procedure(ITypeInfo *typeinfo, FUNCDESC *desc, IScript
 
     proc->IScriptProcedure_iface.lpVtbl = &ScriptProcedureVtbl;
     proc->ref = 1;
+    proc->hash = hash;
     proc->name = str;
+    list_add_tail(proc_list, &proc->entry);
 
     *procedure = &proc->IScriptProcedure_iface;
 
@@ -970,11 +997,16 @@ static ULONG WINAPI ScriptProcedureCollection_Release(IScriptProcedureCollection
 {
     ScriptProcedureCollection *This = impl_from_IScriptProcedureCollection(iface);
     LONG ref = InterlockedDecrement(&This->ref);
+    UINT i;
 
     TRACE("(%p) ref=%d\n", This, ref);
 
     if (!ref)
     {
+        /* Unlink any dangling items from the hash table */
+        for (i = 0; i < ARRAY_SIZE(This->hash_table); i++)
+            list_remove(&This->hash_table[i]);
+
         This->module->procedures = NULL;
         IScriptModule_Release(&This->module->IScriptModule_iface);
         heap_free(This);
@@ -1073,12 +1105,29 @@ static HRESULT WINAPI ScriptProcedureCollection_get_Item(IScriptProcedureCollect
 
     if (V_VT(&index) == VT_BSTR)
     {
+        struct list *proc_list;
+        ScriptProcedure *proc;
         ITypeComp *comp;
         BINDPTR bindptr;
         DESCKIND kind;
         ULONG hash;
+        UINT len;
 
+        len = SysStringLen(V_BSTR(&index));
         hash = LHashValOfNameSys(sizeof(void*) == 8 ? SYS_WIN64 : SYS_WIN32, LOCALE_USER_DEFAULT, V_BSTR(&index));
+        proc_list = &This->hash_table[hash % ARRAY_SIZE(This->hash_table)];
+
+        /* Try to find it in the hash table */
+        LIST_FOR_EACH_ENTRY(proc, proc_list, ScriptProcedure, entry)
+        {
+            if (proc->hash == hash && SysStringLen(proc->name) == len &&
+                !memcmp(proc->name, V_BSTR(&index), len * sizeof(WCHAR)))
+            {
+                IScriptProcedure_AddRef(&proc->IScriptProcedure_iface);
+                *ppdispProcedure = &proc->IScriptProcedure_iface;
+                return S_OK;
+            }
+        }
 
         hr = get_script_typecomp(This->module, typeinfo, &comp);
         if (FAILED(hr)) return hr;
@@ -1089,7 +1138,7 @@ static HRESULT WINAPI ScriptProcedureCollection_get_Item(IScriptProcedureCollect
         switch (kind)
         {
         case DESCKIND_FUNCDESC:
-            hr = get_script_procedure(typeinfo, bindptr.lpfuncdesc, ppdispProcedure);
+            hr = get_script_procedure(This, typeinfo, bindptr.lpfuncdesc, ppdispProcedure);
             ITypeInfo_Release(typeinfo);
             return hr;
         case DESCKIND_IMPLICITAPPOBJ:
@@ -1113,7 +1162,7 @@ static HRESULT WINAPI ScriptProcedureCollection_get_Item(IScriptProcedureCollect
     hr = ITypeInfo_GetFuncDesc(typeinfo, V_INT(&index) - 1, &desc);
     if (FAILED(hr)) return hr;
 
-    return get_script_procedure(typeinfo, desc, ppdispProcedure);
+    return get_script_procedure(This, typeinfo, desc, ppdispProcedure);
 }
 
 static HRESULT WINAPI ScriptProcedureCollection_get_Count(IScriptProcedureCollection *iface, LONG *plCount)
@@ -1343,6 +1392,7 @@ static HRESULT WINAPI ScriptModule_get_Procedures(IScriptModule *iface, IScriptP
     else
     {
         ScriptProcedureCollection *procs;
+        UINT i;
 
         if (!(procs = heap_alloc(sizeof(*procs))))
             return E_OUTOFMEMORY;
@@ -1351,6 +1401,9 @@ static HRESULT WINAPI ScriptModule_get_Procedures(IScriptModule *iface, IScriptP
         procs->ref = 1;
         procs->count = -1;
         procs->module = This;
+        for (i = 0; i < ARRAY_SIZE(procs->hash_table); i++)
+            list_init(&procs->hash_table[i]);
+
         This->procedures = procs;
         IScriptModule_AddRef(&This->IScriptModule_iface);
     }
diff --git a/dlls/msscript.ocx/tests/msscript.c b/dlls/msscript.ocx/tests/msscript.c
index ed4d0f8..c56e2d5 100644
--- a/dlls/msscript.ocx/tests/msscript.c
+++ b/dlls/msscript.ocx/tests/msscript.c
@@ -3293,7 +3293,7 @@ static void test_IScriptControl_get_CodeObject(void)
 static void test_IScriptControl_get_Procedures(void)
 {
     IScriptProcedureCollection *procs, *procs2;
-    IScriptProcedure *proc;
+    IScriptProcedure *proc, *proc2;
     IScriptControl *sc;
     VARIANT var;
     LONG count;
@@ -3527,10 +3527,18 @@ static void test_IScriptControl_get_Procedures(void)
             CHECK_CALLED(GetFuncDesc);
             CHECK_CALLED(GetNames);
             CHECK_CALLED(ReleaseFuncDesc);
-            IScriptProcedure_Release(proc);
 
+            /* The name is cached and not looked up with Bind anymore */
             V_VT(&var) = VT_BSTR;
             V_BSTR(&var) = SysAllocString(custom_engine_funcs[i].name);
+            hr = IScriptProcedureCollection_get_Item(procs, var, &proc2);
+            ok(hr == S_OK, "get_Item for %s failed: 0x%08x.\n", wine_dbgstr_w(custom_engine_funcs[i].name), hr);
+            ok(proc == proc2, "proc and proc2 are not the same for %s and index %u.\n",
+                wine_dbgstr_w(custom_engine_funcs[i].name), i + 1);
+            IScriptProcedure_Release(proc);
+            IScriptProcedure_Release(proc2);
+
+            /* Since both were released, the cache entry is destroyed */
             SET_EXPECT(Bind);
             SET_EXPECT(GetNames);
             SET_EXPECT(ReleaseFuncDesc);
-- 
2.21.0




More information about the wine-devel mailing list