Subject: [PATCH] 升级补丁
---
Index: src/ChainableMockMethodContainer.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/ChainableMockMethodContainer.cpp b/src/ChainableMockMethodContainer.cpp
--- a/src/ChainableMockMethodContainer.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/ChainableMockMethodContainer.cpp	(date 1730193449519)
@@ -23,6 +23,7 @@
 #include <list>
 #include <utility>
 #include <algorithm>
+#include <mutex>
 
 MOCKCPP_NS_START
 
@@ -49,10 +50,14 @@
    InvocationMocker* findInvocationMocker(const std::string& id) const;
 
    void reset();
+   void reset(ChainableMockMethodKey* key);
    void verify();
 
    bool verified;
    List methods;
+
+private:
+   mutable std::mutex mtx;
 };
 
 /////////////////////////////////////////////////////////////////////////
@@ -81,10 +86,30 @@
   }
 }
 
+/////////////////////////////////////////////////////////////////////////
+namespace
+{
+  struct IsMethodKeyMatch
+  {
+    IsMethodKeyMatch(ChainableMockMethodKey* key)
+      : myKey(key)
+    {}
+
+    bool operator()(ChainableMockMethodContainerImpl::ValueType value)
+    {
+       return myKey->equals(getKey(value));
+    }
+
+    ChainableMockMethodKey* myKey;
+  };
+
+}
+
 /////////////////////////////////////////////////////////////////////////
 void
 ChainableMockMethodContainerImpl::reset()
 {
+   std::lock_guard<std::mutex> lock(mtx);
     //for_each(methods.begin(), methods.end(), resetMethod);
 	for(Iterator i = methods.begin(); i != methods.end(); i++)
 	{
@@ -107,6 +132,7 @@
 void
 ChainableMockMethodContainerImpl::verify()
 {
+    std::lock_guard<std::mutex> lock(mtx);
     if(verified) return;
 
     verified = true;
@@ -114,6 +140,20 @@
     for_each(methods.begin(), methods.end(), verifyMethod);
 }
 
+void
+ChainableMockMethodContainerImpl::reset(ChainableMockMethodKey* key)
+{
+   std::lock_guard<std::mutex> lock(mtx);
+   ConstIterator i = std::find_if(methods.begin()
+      , methods.end()
+      , IsMethodKeyMatch(key));
+
+   if (i != methods.end()) {
+      resetMethod(*i);
+      methods.remove(*i);
+   }
+}
+
 /////////////////////////////////////////////////////////////////////////
 ChainableMockMethodContainerImpl::~ChainableMockMethodContainerImpl()
 {
@@ -125,6 +165,7 @@
 ChainableMockMethodContainerImpl::addMethod(ChainableMockMethodKey* key, \
               ChainableMockMethodCore* method)
 {
+    std::lock_guard<std::mutex> lock(mtx);
     methods.push_back(ValueType(key, method));
 }
 
@@ -144,6 +185,7 @@
 InvocationMocker*
 ChainableMockMethodContainerImpl::findInvocationMocker(const std::string& id) const
 {
+   std::lock_guard<std::mutex> lock(mtx);
    for (ConstIterator i = methods.begin(); i != methods.end(); ++i)
    {
 	  InvocationMocker* mocker = i->second->getInvocationMocker(id);
@@ -156,29 +198,11 @@
    return 0;
 }
 
-/////////////////////////////////////////////////////////////////////////
-namespace
-{
-  struct IsMethodKeyMatch
-  {
-    IsMethodKeyMatch(ChainableMockMethodKey* key)
-      : myKey(key)
-    {}
-
-    bool operator()(ChainableMockMethodContainerImpl::ValueType value)
-    {
-       return myKey->equals(getKey(value));
-    }
-
-    ChainableMockMethodKey* myKey;
-  };
-
-}
-
 /////////////////////////////////////////////////////////////////////////
 ChainableMockMethodCore*
 ChainableMockMethodContainerImpl::getMethod(ChainableMockMethodKey* key) const
 {
+    std::lock_guard<std::mutex> lock(mtx);
     ConstIterator i = std::find_if( methods.begin()
                                   , methods.end()
                                   , IsMethodKeyMatch(key));
@@ -227,6 +251,12 @@
    This->verify();
 }
 
+void
+ChainableMockMethodContainer::reset(ChainableMockMethodKey* key)
+{
+   This->reset(key);
+}
+
 /////////////////////////////////////////////////////////////////////////
 MOCKCPP_NS_END
 
Index: tests/ut/TestOutBoundP.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestOutBoundP.h b/tests/ut/TestOutBoundP.h
new file mode 100644
--- /dev/null	(date 1730193412175)
+++ b/tests/ut/TestOutBoundP.h	(date 1730193412175)
@@ -0,0 +1,180 @@
+/***
+    mockcpp is a generic C/C++ mock framework.
+    Copyright (C) <2021>  <Zhao Yonggang: yonggang.zhao@icloud.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <cstring>
+#include <mockcpp/mokc.h>
+#include <testngpp/testngpp.hpp>
+
+
+
+USING_TESTNGPP_NS
+USING_MOCKCPP_NS
+
+void foo(char *out) {
+  *out = 0;
+}
+
+void bar(int *out) {
+  out[0] = 0;
+  out[1] = 1;
+  out[2] = 2;
+}
+
+void baz(int *out) {
+  *out = 0;
+}
+
+FIXTURE(OutBoundP) 
+{
+  TEST(outBoundP should not outBoundP different type with input parameter)
+  {
+    unsigned char array[10] = "hostname";
+    MOCKER(foo).stubs().with(outBoundP(array, std::strlen((char *)array))).will(returnValue(10));
+    char buffer[10] = {0};
+    ASSERT_THROWS_ANYTHING(foo(buffer));
+    GlobalMockObject::verify();
+  }
+
+  TEST(outBoundP for array output parameter)
+  {
+    int array[] = {0, 10, 20};
+    MOCKER(bar).stubs().with(outBoundP(array, sizeof(array)));
+    int out[3];
+    bar(out);
+    ASSERT_EQ(out[0], 0);
+    ASSERT_EQ(out[1], 10);
+    ASSERT_EQ(out[2], 20);
+    GlobalMockObject::verify();
+  }
+
+  TEST(outBoundP for output value parameter)
+  {
+    int val = 10;
+    MOCKER(baz).stubs().with(outBoundP(&val));
+    int out;
+    baz(&out);
+    ASSERT_EQ(out, 10);
+    GlobalMockObject::verify();
+  }
+
+	TEST(outBoundP reset specified function for different prototype 1)
+	{
+    char c = 10;
+    int i = 20;
+		MOCKER(foo).stubs().with(outBoundP(&c));
+		MOCKER(baz).stubs().with(outBoundP(&i));
+    char outc;
+    int outi;
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(10,  outc);
+		ASSERT_EQ(20,  outi);
+		GlobalMockObject::reset((const void *)foo);
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(0,  outc);
+		ASSERT_EQ(20,  outi);
+    char c1 = 100;
+		MOCKER(foo).stubs().with(outBoundP(&c1));
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(100,  outc);
+		ASSERT_EQ(20,  outi);
+    GlobalMockObject::verify();
+	}
+
+	TEST(outBoundP reset specified function for different prototype 2)
+	{
+    char c = 10;
+    int i = 20;
+		MOCKER(foo).stubs().with(outBoundP(&c));
+		MOCKER(baz).stubs().with(outBoundP(&i));
+    char outc;
+    int outi;
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(10,  outc);
+		ASSERT_EQ(20,  outi);
+		GlobalMockObject::reset((const void *)baz);
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(10,  outc);
+		ASSERT_EQ(0,  outi);
+    int i1 = 200;
+		MOCKER(baz).stubs().with(outBoundP(&i1));
+    foo(&outc);
+    baz(&outi);
+		ASSERT_EQ(10,  outc);
+		ASSERT_EQ(200,  outi);
+    GlobalMockObject::verify();
+	}
+
+	TEST(outBoundP reset specified function for same prototype 1)
+	{
+    int r = 10;
+    int z = 20; 
+		MOCKER(bar).stubs().with(outBoundP(&r));
+		MOCKER(baz).stubs().with(outBoundP(&z));
+    int outr[3];
+    int outz;
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(10,  outr[0]);
+		ASSERT_EQ(20,  outz);
+		GlobalMockObject::reset((const void *)bar);
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(0,  outr[0]);
+		ASSERT_EQ(1,  outr[1]);
+		ASSERT_EQ(2,  outr[2]);
+		ASSERT_EQ(20,  outz);
+    int r1 = 100;
+		MOCKER(bar).stubs().with(outBoundP(&r1));
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(100,  outr[0]);
+		ASSERT_EQ(20,  outz);
+    GlobalMockObject::verify();
+	}
+
+	TEST(outBoundP reset specified function for same prototype 2)
+	{
+    int r = 10;
+    int z = 20; 
+		MOCKER(bar).stubs().with(outBoundP(&r));
+		MOCKER(baz).stubs().with(outBoundP(&z));
+    int outr[3];
+    int outz;
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(10,  outr[0]);
+		ASSERT_EQ(20,  outz);
+		GlobalMockObject::reset((const void *)baz);
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(10,  outr[0]);
+		ASSERT_EQ(0,  outz);
+    int z1 = 200; 
+		MOCKER(baz).stubs().with(outBoundP(&z1));
+    bar((int *)outr);
+    baz(&outz);
+		ASSERT_EQ(10,  outr[0]);
+		ASSERT_EQ(200,  outz);
+    GlobalMockObject::verify();
+	}
+};
Index: tests/ut/TestMockMutlThread.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMockMutlThread.h b/tests/ut/TestMockMutlThread.h
new file mode 100644
--- /dev/null	(date 1730193411394)
+++ b/tests/ut/TestMockMutlThread.h	(date 1730193411394)
@@ -0,0 +1,79 @@
+/***
+    mockcpp is a generic C/C++ mock framework.
+    Copyright (C) <2009>  <Darwin Yuan: darwin.yuan@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <mockcpp/mokc.h>
+#include <testcpp/testcpp.hpp>
+#include <thread>
+#include <unistd.h>
+
+USING_MOCKCPP_NS
+
+int foo(int *a)
+{
+    *a = 1;
+    return 1;
+}
+
+int bar(int *a)
+{
+    *a = 2;
+    return 2;
+}
+
+int check_ret_cnt;
+void check_ret()
+{
+    check_ret_cnt = 0;
+    for (int i = 0; i < 10001; i++) {
+        int out;
+        int ret = foo(&out);
+        check_ret_cnt++;
+        usleep(1);
+    }
+}
+
+int check_out_cnt;
+void check_out()
+{
+    check_out_cnt = 0;
+    for (int i = 0; i < 9999; i++) {
+        int out;
+        int ret = foo(&out);
+        check_out_cnt++;
+        usleep(1);
+    }
+}
+
+class TestMockMutlThread : public TESTCPP_NS::TestFixture
+{
+public:
+	void setUp() { }
+	void tearDown() { }
+
+    void testConcurrentExecutionWithoutThrowingExceptions()
+    {
+        MOCKER(foo).stubs().will(invoke(bar));
+        std::thread thread1(check_ret);
+        std::thread thread2(check_out);
+        thread1.join();
+        thread2.join();
+        TS_ASSERT_EQUALS(check_ret_cnt, 10001);
+        TS_ASSERT_EQUALS(check_out_cnt, 9999);
+        GlobalMockObject::verify();
+    }
+};
Index: src/GlobalMockObject.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/GlobalMockObject.cpp b/src/GlobalMockObject.cpp
--- a/src/GlobalMockObject.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/GlobalMockObject.cpp	(date 1730193448131)
@@ -47,5 +47,10 @@
     instance.reset();
 }
 
+void GlobalMockObject::reset(const void *api)
+{
+    instance.reset(api);
+}
+
 MOCKCPP_NS_END
 
Index: include/mockcpp/ApiHookFunctor.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/ApiHookFunctor.h b/include/mockcpp/ApiHookFunctor.h
--- a/include/mockcpp/ApiHookFunctor.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/ApiHookFunctor.h	(date 1730193425395)
@@ -18,6 +18,7 @@
 #ifndef __MOCKCPP_API_HOOK_FUNCTOR_H__
 #define __MOCKCPP_API_HOOK_FUNCTOR_H__
 
+#include <mutex>
 #include <mockcpp/mockcpp.h>
 #include <mockcpp/GlobalMockObject.h>
 
@@ -39,8 +40,9 @@
  \
    static R CallingConvention hook(DECL_PARAMS_LIST(n)) \
    { \
-      return GlobalMockObject::instance.invoke<R>(apiAddress) \
-                                (empty_caller DECL_REST_PARAMS(n)); \
+      auto func = GlobalMockObject::instance.invoke<R>(apiAddress); \
+      std::lock_guard<std::mutex> lock(mtx); \
+      return func(empty_caller DECL_REST_PARAMS(n)); \
    } \
  \
    static bool appliedBy(F* api) \
@@ -77,23 +79,26 @@
 private: \
    static void* apiAddress; \
    static unsigned int refCount; \
+   static std::mutex mtx; \
 }; \
 template <typename R DECL_TEMPLATE_ARGS(n), unsigned int Seq> \
 void* ApiHookFunctor<R CallingConvention (DECL_ARGS(n)), Seq>::apiAddress = 0; \
+template <typename R DECL_TEMPLATE_ARGS(n), unsigned int Seq> \
+std::mutex ApiHookFunctor<R CallingConvention (DECL_ARGS(n)), Seq>::mtx; \
 template <typename R DECL_TEMPLATE_ARGS(n), unsigned int Seq> \
-unsigned int ApiHookFunctor<R CallingConvention (DECL_ARGS(n)), Seq>::refCount = 0 
+unsigned int ApiHookFunctor<R CallingConvention (DECL_ARGS(n)), Seq>::refCount = 0
 
 #if defined(_MSC_VER)
 // TODO: ApiHook related tests failed on VS2019.
 // [  ERROR   ] TestApiHook.h:66: hardware exception STATUS_ILLEGAL_INSTRUCTION raised in setup or running test
 // [  ERROR   ] TestApiHook.h:66 : hardware exception STATUS_ACCESS_VIOLATION raised in teardown
-#if _MSC_VER >= 1920    // VS 2019 
+#if _MSC_VER >= 1920    // VS 2019
 #define MOCKCPP_API_HOOK_FUNCTOR_DEF(n) \
 __MOCKCPP_API_HOOK_FUNCTOR_DEF(n, __stdcall)
 #else
 #define MOCKCPP_API_HOOK_FUNCTOR_DEF(n) \
 __MOCKCPP_API_HOOK_FUNCTOR_DEF(n, ); \
-__MOCKCPP_API_HOOK_FUNCTOR_DEF(n, __stdcall) 
+__MOCKCPP_API_HOOK_FUNCTOR_DEF(n, __stdcall)
 #endif
 #else
 #define MOCKCPP_API_HOOK_FUNCTOR_DEF(n) \
Index: tests/ut/TestMockClassPublic.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMockClassPublic.h b/tests/ut/TestMockClassPublic.h
new file mode 100644
--- /dev/null	(date 1730193409856)
+++ b/tests/ut/TestMockClassPublic.h	(date 1730193409856)
@@ -0,0 +1,114 @@
+/***
+    mockcpp is a generic C/C++ mock framework.
+    Copyright (C) <2009>  <Darwin Yuan: darwin.yuan@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <testcpp/testcpp.hpp>
+#include <mockcpp/mokc.h>
+
+USING_MOCKCPP_NS
+
+class mytest {
+public:
+    int func(int a, int b, int c, int d);
+    int func_void();
+};
+
+class stub {
+public:
+    static int func(void *p, int a, int b, int c, int d);
+    static int func_void(void *p);
+};
+
+int mytest::func(int a, int b, int c, int d)
+{
+    return a + b + c + d;
+}
+
+int mytest::func_void()
+{
+    int arr[2] = {10, 20};
+    return arr[0] + arr[1];
+}
+
+int stub::func(void *p, int a, int b, int c, int d)
+{
+    return 2 * (a + b + c + d);
+}
+
+int stub::func_void(void *p)
+{
+    int arr[2] = {10, 20};
+    return 2 * (arr[0] + arr[1]);
+}
+
+int stub_func(void *p, int a, int b, int c, int d)
+{
+    return 2 * (a + b + c + d);
+}
+
+class TestMockClassPublic : public TESTCPP_NS::TestFixture
+{
+public:
+	void setUp() { }
+	void tearDown() { }
+
+/*
+LLVM兼容绝大部分GNU的功能,但是对于一些认为不合理的功能则选择永不支持。此处将成员函数地址类型强转为一般函数地址类型即是此类功能。
+如下url的信息展示了不支持此类功能的具体解释: https://github.com/llvm/llvm-project/issues/22495
+*/
+#ifndef __clang__
+    void testMockWithClassStaticMember()
+    {
+        mytest inst;
+        MOCKER_CPP(&mytest::func, int (*)(void *, int, int, int, int)).stubs().will(invoke(stub::func));
+        int ret = inst.func(10, 20, 30, 40);
+        TS_ASSERT(ret == 200);
+        GlobalMockObject::verify();        
+    }
+
+    void testMockWithNormalCFunction()
+    {
+        mytest inst;
+        MOCKER_CPP(&mytest::func, int (*)(void *, int, int, int, int)).stubs().will(invoke(stub_func));
+        int ret = inst.func(10, 20, 30, 40);
+        TS_ASSERT(ret == 200);
+        GlobalMockObject::verify();
+    }
+
+    void testMockWithClassStaticMemberVoid()
+    {   
+        mytest inst;
+        MOCKER_CPP(&mytest::func_void, int (*)(void *)).stubs().will(invoke(stub::func_void));
+        int ret = inst.func_void();
+        TS_ASSERT(ret == 60);
+        GlobalMockObject::verify();    
+    }
+#else
+    void testMockWithClassStaticMember()
+    {
+    }
+
+    void testMockWithNormalCFunction()
+    {
+    }
+
+    void testMockWithClassStaticMemberVoid()
+    {   
+    }
+#endif
+};
+
Index: include/mockcpp/VirtualTable.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/VirtualTable.h b/include/mockcpp/VirtualTable.h
--- a/include/mockcpp/VirtualTable.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/VirtualTable.h	(date 1730193417511)
@@ -41,6 +41,7 @@
 
    void addMethod(void* methodAddr, unsigned int indexOfVtbl, unsigned int indexofVptr = 0);
    void setDestructor(unsigned int vptrIndex, unsigned int vtblIndex);
+   void reset(unsigned int vptrIndex, unsigned int vtblIndex);
 
    void expectsBeingDeleted();
    void expectsKeepAlive();
Index: tests/ut/TestMockClassPublic.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMockClassPublic.cpp b/tests/ut/TestMockClassPublic.cpp
new file mode 100644
--- /dev/null	(date 1730193410497)
+++ b/tests/ut/TestMockClassPublic.cpp	(date 1730193410497)
@@ -0,0 +1,226 @@
+#include <testngpp/internal/TestCase.h>
+#include <testngpp/internal/TestFixtureDesc.h>
+#include <testngpp/internal/TestSuiteDesc.h>
+#include <testngpp/internal/DataDriven.h>
+#include "TestMockClassPublic.h"
+
+static struct TESTCASE_TestMockClassPublic_testMockWithClassStaticMember
+   : public TESTNGPP_NS::TestCase
+{
+   TESTCASE_TestMockClassPublic_testMockWithClassStaticMember()
+      : TESTNGPP_NS::TestCase
+        ( "testMockWithClassStaticMember"
+        , "TestMockClassPublic"
+        , "TestMockClassPublic"
+        , 0
+        , "TestMockClassPublic.h"
+        , 69)
+   {}
+
+   void setFixture(TESTNGPP_NS::TestFixture* fixture)
+   {
+      if(fixture == 0)
+      {
+         belongedFixture = new TestMockClassPublic();
+      }
+      else
+      {
+         belongedFixture = dynamic_cast<TestMockClassPublic*>(fixture);
+      }
+   }
+
+   void runTest()
+   {
+      
+belongedFixture->testMockWithClassStaticMember()
+;
+   }
+
+   TESTNGPP_NS::TestFixture* getFixture() const
+   {
+      return belongedFixture;
+   }
+
+   unsigned int numberOfTags() const
+   {
+      return 0;
+   }
+
+   const char** getTags() const
+   {
+      static const char* tags[] = {0};
+      return tags;
+   }
+
+   const char* getMemCheckSwitch() const
+   {
+      static const char* memCheckSwitch = "none";
+      return memCheckSwitch;
+   }
+
+private:
+   TestMockClassPublic* belongedFixture;
+} testcase_instance_TestMockClassPublic_testMockWithClassStaticMember ;
+
+
+
+static struct TESTCASE_TestMockClassPublic_testMockWithNormalCFunction
+   : public TESTNGPP_NS::TestCase
+{
+   TESTCASE_TestMockClassPublic_testMockWithNormalCFunction()
+      : TESTNGPP_NS::TestCase
+        ( "testMockWithNormalCFunction"
+        , "TestMockClassPublic"
+        , "TestMockClassPublic"
+        , 0
+        , "TestMockClassPublic.h"
+        , 78)
+   {}
+
+   void setFixture(TESTNGPP_NS::TestFixture* fixture)
+   {
+      if(fixture == 0)
+      {
+         belongedFixture = new TestMockClassPublic();
+      }
+      else
+      {
+         belongedFixture = dynamic_cast<TestMockClassPublic*>(fixture);
+      }
+   }
+
+   void runTest()
+   {
+      
+belongedFixture->testMockWithNormalCFunction()
+;
+   }
+
+   TESTNGPP_NS::TestFixture* getFixture() const
+   {
+      return belongedFixture;
+   }
+
+   unsigned int numberOfTags() const
+   {
+      return 0;
+   }
+
+   const char** getTags() const
+   {
+      static const char* tags[] = {0};
+      return tags;
+   }
+
+   const char* getMemCheckSwitch() const
+   {
+      static const char* memCheckSwitch = "none";
+      return memCheckSwitch;
+   }
+
+private:
+   TestMockClassPublic* belongedFixture;
+} testcase_instance_TestMockClassPublic_testMockWithNormalCFunction ;
+
+
+
+static struct TESTCASE_TestMockClassPublic_testMockWithClassStaticMemberVoid
+   : public TESTNGPP_NS::TestCase
+{
+   TESTCASE_TestMockClassPublic_testMockWithClassStaticMemberVoid()
+      : TESTNGPP_NS::TestCase
+        ( "testMockWithClassStaticMemberVoid"
+        , "TestMockClassPublic"
+        , "TestMockClassPublic"
+        , 0
+        , "TestMockClassPublic.h"
+        , 87)
+   {}
+
+   void setFixture(TESTNGPP_NS::TestFixture* fixture)
+   {
+      if(fixture == 0)
+      {
+         belongedFixture = new TestMockClassPublic();
+      }
+      else
+      {
+         belongedFixture = dynamic_cast<TestMockClassPublic*>(fixture);
+      }
+   }
+
+   void runTest()
+   {
+      
+belongedFixture->testMockWithClassStaticMemberVoid()
+;
+   }
+
+   TESTNGPP_NS::TestFixture* getFixture() const
+   {
+      return belongedFixture;
+   }
+
+   unsigned int numberOfTags() const
+   {
+      return 0;
+   }
+
+   const char** getTags() const
+   {
+      static const char* tags[] = {0};
+      return tags;
+   }
+
+   const char* getMemCheckSwitch() const
+   {
+      static const char* memCheckSwitch = "none";
+      return memCheckSwitch;
+   }
+
+private:
+   TestMockClassPublic* belongedFixture;
+} testcase_instance_TestMockClassPublic_testMockWithClassStaticMemberVoid ;
+
+
+
+static TESTNGPP_NS::TestCase* g_TESTCASEARRAY_TestMockClassPublic[] = {
+&testcase_instance_TestMockClassPublic_testMockWithClassStaticMember,
+&testcase_instance_TestMockClassPublic_testMockWithNormalCFunction,
+&testcase_instance_TestMockClassPublic_testMockWithClassStaticMemberVoid,
+0
+};
+
+
+
+
+/*static*/ TESTNGPP_NS::TestFixtureDesc test_fixture_desc_instance_TestMockClassPublic
+   ( "TestMockClassPublic"
+   , "TestMockClassPublic.h"
+   , g_TESTCASEARRAY_TestMockClassPublic
+   , (sizeof(g_TESTCASEARRAY_TestMockClassPublic)/sizeof(g_TESTCASEARRAY_TestMockClassPublic[0])) - 1
+   );
+
+
+
+static TESTNGPP_NS::TestFixtureDesc* array_of_fixture_desc_TestMockClassPublic[] = {
+&test_fixture_desc_instance_TestMockClassPublic,
+0
+};
+
+
+
+
+static TESTNGPP_NS::TestSuiteDesc test_suite_desc_instance_TestMockClassPublic
+   ( "TestMockClassPublic"
+   , array_of_fixture_desc_TestMockClassPublic
+   , (sizeof(array_of_fixture_desc_TestMockClassPublic)/sizeof(array_of_fixture_desc_TestMockClassPublic[0])) - 1
+   );
+
+
+
+extern "C" DLL_EXPORT TESTNGPP_NS::TestSuiteDesc* ___testngpp_test_suite_desc_getter() {
+   return &test_suite_desc_instance_TestMockClassPublic;
+}
+
+
Index: include/mockcpp/OutBoundPointer.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/OutBoundPointer.h b/include/mockcpp/OutBoundPointer.h
--- a/include/mockcpp/OutBoundPointer.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/OutBoundPointer.h	(date 1730193423572)
@@ -59,6 +59,11 @@
 
     bool evalSelf(const RefAny& val) const
     {
+      if(!any_castable<T*>(val))
+      {
+        return false;
+      }
+
       T* p = any_cast<T*>(val);
       if (p == 0)
       {
Index: CMakeLists.txt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/CMakeLists.txt	(date 1730193430494)
@@ -4,6 +4,7 @@
 PROJECT(mockcpp)
 
 INCLUDE(ProjectVar.txt)
+add_compile_options(-D_GLIBCXX_USE_CXX11_ABI=0)
 
 ADD_SUBDIRECTORY(src)
 
Index: include/mockcpp/GnuMethodInfoReader.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/GnuMethodInfoReader.h b/include/mockcpp/GnuMethodInfoReader.h
--- a/include/mockcpp/GnuMethodInfoReader.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/GnuMethodInfoReader.h	(date 1730193424820)
@@ -53,6 +53,8 @@
 	MethodDescriptionUnion<Method> m;
 	m.method = input;
 
+/* 在arm32平台thumb指令集下, 函数指针地址可以是奇数 */
+#ifndef __arm__
    oss_t oss;
    oss << "Method address should be even, please make sure the method "
        << TypeString<Method>::value() << " is NOT a virtual method";
@@ -60,7 +62,7 @@
 	MOCKCPP_ASSERT_TRUE(
 		oss.str(),
 	   !(m.desc.u.index%2));
-
+#endif
 
    return m.desc.u.addr;
 }
@@ -73,13 +75,24 @@
 	MethodDescriptionUnion<ExpectedMethodType> m;
 	m.method = input;
 
+// 在arm64和arm32平台下虚函数的 Method 与x86平台的值不同,需要做特殊处理
+#if defined(__aarch64__) || defined(__arm__)
+   m.desc.u.index++;
+   if (m.desc.delta >= 16)
+      m.desc.delta -= sizeof(void *);
+#endif
+
+/* 在arm32平台thumb指令集下, 虚函数地址不适用这个检查 */
+#ifndef __arm__
    oss_t oss;
    oss << "Virtual method address should be odd, please make sure the method "
-       << TypeString<Method>::value() << " is a virtual method";
+       << TypeString<Method>::value() << " is a virtual method. index is "
+       << m.desc.u.index << ", delta is " << m.desc.delta << ".";
  
 	MOCKCPP_ASSERT_TRUE(
 		oss.str(),
-	   (m.desc.u.index%2));
+	   (m.desc.u.index%2) && (m.desc.delta < 16 && m.desc.delta >= 0));
+#endif
 
 	return m.desc;
 }

Index: build.sh
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/build.sh b/build.sh
--- a/build.sh	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/build.sh	(date 1730193414222)
@@ -23,8 +23,14 @@
 USER_CHOOSED_COMPILER_MAJOR_VER=""
 
 # script usage 1: no parameter, use GNU compiler
+if [ "$1" == "llvm" ]; then
+AUTO_COMPILER="LLVM"
+AUTO_CXX_VER=`clang -dumpversion | awk -F.  '{print $1}'`
+COMPILER_DEF="-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
+else
 AUTO_COMPILER="GNU"
 AUTO_CXX_VER=`gcc -dumpversion | awk -F.  '{print $1}'`
+fi
 InitEnviroment $AUTO_COMPILER $AUTO_CXX_VER
 
 # script usage 2: input the compiler name and major version
@@ -42,9 +48,9 @@
 
 #if [ $3 != "test" ]; then
 
-cmake  -S . -B $BUILD_DIR/mockcpp
-cmake  -S tests/3rdparty/testngpp -B $BUILD_DIR/mockcpp_testngpp
-cmake  -S tests -B $BUILD_DIR/mockcpp_tests
+cmake $COMPILER_DEF -S . -B $BUILD_DIR/mockcpp
+cmake $COMPILER_DEF -S tests/3rdparty/testngpp -B $BUILD_DIR/mockcpp_testngpp
+cmake $COMPILER_DEF -S tests -B $BUILD_DIR/mockcpp_tests
 
 CompileProject $MY_CXX_COMPILER_NAME $BUILD_DIR/mockcpp
 CompileProject $MY_CXX_COMPILER_NAME $BUILD_DIR/mockcpp_testngpp
Index: src/UnixCodeModifier.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/UnixCodeModifier.cpp b/src/UnixCodeModifier.cpp
--- a/src/UnixCodeModifier.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/UnixCodeModifier.cpp	(date 1730193447936)
@@ -18,21 +18,20 @@
 #include <string.h>
 #include <inttypes.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include <mockcpp/CodeModifier.h>
 
-#define PAGE_ALIGN_BITS  12
-
 //////////////////////////////////////////////////////////////////
-#define PAGE_SIZE   (1 << PAGE_ALIGN_BITS)
-#define ALIGN_TO_PAGE_BOUNDARY(addr) (void*) (((uintptr_t)addr) & (~((1<<(PAGE_ALIGN_BITS))-1)))
+#define ALIGN_TO_PAGE_BOUNDARY(addr, page_size) (void*) (((uintptr_t)addr) & (~(page_size - 1)))
 //////////////////////////////////////////////////////////////////
 
 MOCKCPP_NS_START
 
 bool CodeModifier::modify(void *dest, const void *src, size_t size)
 {
-    if(::mprotect(ALIGN_TO_PAGE_BOUNDARY(dest), PAGE_SIZE * 2, PROT_EXEC | PROT_WRITE | PROT_READ ) != 0)
+    int page_size = getpagesize();
+    if(::mprotect(ALIGN_TO_PAGE_BOUNDARY(dest, page_size), page_size * 2, PROT_EXEC | PROT_WRITE | PROT_READ ) != 0)
     {  
        return false; 
     }
Index: include/mockcpp/mokc.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/mokc.h b/include/mockcpp/mokc.h
--- a/include/mockcpp/mokc.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/mokc.h	(date 1730193424876)
@@ -30,6 +30,7 @@
 #    define MOCKER(api) MOCKCPP_NS::mockAPI(#api, api)
 #  endif
 
+#  define MOCKER_CPP(api, TT) (MOCKCPP_NS::mockAPI((#api), (reinterpret_cast<TT>(api))))
 USING_MOCKCPP_NS
 
 #endif
Index: src/JmpCodeArch.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCodeArch.h b/src/JmpCodeArch.h
--- a/src/JmpCodeArch.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/JmpCodeArch.h	(date 1730193449827)
@@ -19,10 +19,14 @@
 
 #include <mockcpp/mockcpp.h>
 
-#if BUILD_FOR_X64
+#if defined(BUILD_FOR_X64)
 # include "JmpCodeX64.h"
-#elif BUILD_FOR_X86
+#elif defined(BUILD_FOR_X86)
 # include "JmpCodeX86.h"
+#elif defined(BUILD_FOR_AARCH64)
+# include "JmpCodeAArch64.h"
+#elif defined(BUILD_FOR_AARCH32)
+# include "JmpCodeAArch32.h"
 #endif
 
 #endif
Index: include/mockcpp/HookMockObject.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/HookMockObject.h b/include/mockcpp/HookMockObject.h
--- a/include/mockcpp/HookMockObject.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/HookMockObject.h	(date 1730193421491)
@@ -47,6 +47,7 @@
     }
 
     void reset();
+    void reset(const void* api);
 
 private:
 
Index: src/MockObjectBase.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/MockObjectBase.cpp b/src/MockObjectBase.cpp
--- a/src/MockObjectBase.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/MockObjectBase.cpp	(date 1730193451194)
@@ -46,6 +46,7 @@
    ChainableMockMethodContainer* container;
 
    void reset();
+   void reset(unsigned int vptrIndex, unsigned int vtblIndex);
 
 private:
 
@@ -107,6 +108,13 @@
   
     return method;
 }
+////////////////////////////////////////////////////////////////////////
+void MockObjectBaseImpl::reset(unsigned int vptrIndex, unsigned int vtblIndex)
+{
+    ChainableMockMethodIndexKey* key = \
+             new ChainableMockMethodIndexKey(vptrIndex, vtblIndex);
+    container->reset(key);
+}
 ////////////////////////////////////////////////////////////////////////
 ChainableMockMethodCore*
 MockObjectBaseImpl::getMethod(const std::string& name, void* addr, \
@@ -177,6 +185,14 @@
    This->vtbl->setDestructor(vptrIndex, vtblIndex);
 }
 
+////////////////////////////////////////////////////////////////////////
+void MockObjectBase::
+resetVirtualMethod(unsigned int vptrIndex, unsigned int vtblIndex)
+{
+   This->vtbl->reset(vptrIndex, vtblIndex);
+   This->reset(vptrIndex, vtblIndex);
+}
+
 ////////////////////////////////////////////////////////////////////////
 void MockObjectBase::expectsBeingDeleted()
 {
Index: include/mockcpp/ChainingMockHelper.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/ChainingMockHelper.h b/include/mockcpp/ChainingMockHelper.h
--- a/include/mockcpp/ChainingMockHelper.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/ChainingMockHelper.h	(date 1730193416701)
@@ -103,6 +103,12 @@
     typedef T ParaType;
 };
 
+template <typename Predict, typename T>
+struct PredictTypeTraits<bool (Predict::*)(T) const>
+{
+    typedef T ParaType;
+};
+
 template <typename Predict>
 Constraint* checkWith(Predict pred)
 {
Index: src/JmpCode.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCode.cpp b/src/JmpCode.cpp
--- a/src/JmpCode.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/JmpCode.cpp	(date 1730193448920)
@@ -30,6 +30,7 @@
    ////////////////////////////////////////////////
    JmpCodeImpl(const void* from, const void* to)
    {
+      m_from = from;
       ::memcpy(m_code, jmpCodeTemplate, JMP_CODE_SIZE);
       SET_JMP_CODE(m_code, from, to);
    }
@@ -46,9 +47,14 @@
       return JMP_CODE_SIZE;
    }
 
+   void flushCache() const
+   {
+      FLUSH_CACHE(m_from, JMP_CODE_SIZE);
+   }
    ////////////////////////////////////////////////
 
    unsigned char m_code[JMP_CODE_SIZE];
+   const void* m_from;
 };
 
 ///////////////////////////////////////////////////
@@ -77,5 +83,11 @@
    return This->getCodeSize();
 }
 
+void
+  JmpCode::flushCache() const
+{
+  This->flushCache();
+}
+
 MOCKCPP_NS_END
 
Index: src/JmpCodeX64.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCodeX64.h b/src/JmpCodeX64.h
--- a/src/JmpCodeX64.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/JmpCodeX64.h	(date 1730193453131)
@@ -27,5 +27,7 @@
        *(uintptr_t *)(base + 6) = (uintptr_t)to; \
    } while(0)
 
+#define FLUSH_CACHE(from, length) do {} while (0)
+
 #endif
 
Index: include/mockcpp/mockcpp.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/mockcpp.h b/include/mockcpp/mockcpp.h
--- a/include/mockcpp/mockcpp.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/mockcpp.h	(date 1730193426052)
@@ -38,20 +38,40 @@
 # define MOCKCPP_EXPORT 
 #endif
 
+#if  defined(__aarch64__)
+
+#define BUILD_FOR_AARCH64
 
-#if  ( defined (__LP64__) \
+#elif defined(__arm__)
+#include <cstdint>
+#define BUILD_FOR_AARCH32
+
+/*
+ * 参考《DDI0406C_d_armv7ar_arm.pdf》 A4.1 About the instruction sets
+ * armv7总共有4中指令集:
+ * thumb指令集: 主要指令集, 16bit/32bit, 能够自由的和arm指令集切换。
+ * arm指令集: 主要指令集, 32bit, 能够自由的和thumb指令集切换。
+ * thumbee: thumb的变种,用于动态代码生成,不能与thumb和arm自由切换。
+ * Jazelle: 扩展指令, 使得arm处理器能够直接执行一些java字节码。
+ *
+ * 此处只适配了主流的thumb和arm指令集。
+ * 参考A2.3.1 Writing to the PC 和 https://stackoverflow.com/questions/37004954/function-address-in-arm-assembly-have-one-byte-offset
+ * 在仅考虑arm和thumb指令集的前提下, 区分这两种指令集的方式是:thumb指令集下, 函数指针地址是奇数, arm指令集下函数指针地址是偶数。
+ */
+#define IS_USE_THUMB_INST_SET(ptr) (((uintptr_t)(ptr) % 2) != 0)
+#define CONVERT_THUMB_INST_SET_FUNC_POINTER(ptr) ((void *)((uintptr_t)ptr - 1))
+
+#elif  ( defined (__LP64__) \
     || defined (__64BIT__) \
     || defined (_LP64) \
     || ((defined(__WORDSIZE)) && (__WORDSIZE == 64)) \
 	|| defined(WIN64))
 
-#define BUILD_FOR_X64 1
-#define BUILD_FOR_X86 0
+#define BUILD_FOR_X64
 
 #else	
 
-#define BUILD_FOR_X64 0
-#define BUILD_FOR_X86 1
+#define BUILD_FOR_X86
 
 #endif

Index: include/mockcpp/JmpCode.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/JmpCode.h b/include/mockcpp/JmpCode.h
--- a/include/mockcpp/JmpCode.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/JmpCode.h	(date 1730193423652)
@@ -33,6 +33,7 @@
     
     void*  getCodeData() const;
     size_t getCodeSize() const;
+    void flushCache() const;
 private:
 	JmpCodeImpl* This;
 };
Index: src/HookMockObject.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/HookMockObject.cpp b/src/HookMockObject.cpp
--- a/src/HookMockObject.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/HookMockObject.cpp	(date 1730193449016)
@@ -44,6 +44,7 @@
    getMethod(const void* api);
    
    void reset();
+   void reset(const void* api);
 
    ChainableMockMethodContainer* container;
 
@@ -64,6 +65,13 @@
     container->reset();
 }
 
+void
+HookMockObjectImpl::reset(const void *api)
+{
+    ApiHookKey key(api);
+    container->reset(&key);
+}
+
 //////////////////////////////////////////////////////////////
 ChainableMockMethodCore*
 HookMockObjectImpl::
@@ -144,6 +152,11 @@
    This->reset();
 }
 
+void HookMockObject::reset(const void* api)
+{
+   This->reset(api);
+}
+
 //////////////////////////////////////////////////////////////
 
 MOCKCPP_NS_END

Index: tests/ut/TestMockcppSample.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMockcppSample.h b/tests/ut/TestMockcppSample.h
--- a/tests/ut/TestMockcppSample.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestMockcppSample.h	(date 1730193410429)
@@ -46,6 +46,7 @@
     virtual ~Base(){}
     virtual int method1(int a) { return 0; }
     int method2(float b) { return 1; }
+    virtual int method3(int a) = 0;
     int member;
     int member2;
     int member3;
@@ -55,6 +56,7 @@
 public:
    virtual ~Derived(){}
    virtual int method1(int a) { return 10; }
+   virtual int method3(int a) { return 20; }
 };
 
 FIXTURE(mockcpp_sample, mockcpp samples)
@@ -86,6 +88,50 @@
         ASSERT_EQ(1000, Interface::func());
         GlobalMockObject::verify();
     }
+
+    TEST(can reset specified static function)
+    {
+        MockObject<Interface> mocker;
+        MOCK_METHOD(mocker, method)
+            .stubs()
+            .will(returnValue(10));
+        MOCKER(Interface::func)
+            .stubs()
+            .will(returnValue(20));
+        ASSERT_EQ(10, mocker->method());
+        ASSERT_EQ(20, Interface::func());
+        GlobalMockObject::reset((const void *)Interface::func);
+        ASSERT_EQ(10, mocker->method());
+        ASSERT_EQ(0, Interface::func());
+        MOCKER(Interface::func)
+            .stubs()
+            .will(returnValue(200));
+        ASSERT_EQ(10, mocker->method());
+        ASSERT_EQ(200, Interface::func());
+        GlobalMockObject::verify();
+    }
+
+    TEST(can reset specified pure virtual function)
+    {
+        MockObject<Interface> mocker;
+        MOCK_METHOD(mocker, method)
+            .stubs()
+            .will(returnValue(10));
+        MOCKER(Interface::func)
+            .stubs()
+            .will(returnValue(20));
+        ASSERT_EQ(10, mocker->method());
+        ASSERT_EQ(20, Interface::func());
+        mocker.reset(&Interface::method);
+        ASSERT_THROWS_ANYTHING(mocker->method());
+        ASSERT_EQ(20, Interface::func());
+        MOCK_METHOD(mocker, method)
+            .stubs()
+            .will(returnValue(100));
+        ASSERT_EQ(100, mocker->method());
+        ASSERT_EQ(20, Interface::func());
+        GlobalMockObject::verify();
+    }
 
     // mock的类不全是虚方法, 如果调用到非虚方法,则mock框架不会报错,而是调用真正的非虚方法,可能出现内存非法访问。
     // 从设计解耦的角度讲,应尽可能面向接口编程,而需要mock的也都是接口。接口里面的方法全是虚方法。所以不存在这种情况。
@@ -111,6 +157,50 @@
         ASSERT_EQ(8, sizeof(void*));
     }
 
+    TEST(can reset specified virtual function in same mock object)
+    {
+        MockObject<Derived> mocker;
+        MOCK_METHOD(mocker, method1)
+            .stubs()
+            .will(returnValue(100));
+        MOCK_METHOD(mocker, method3)
+            .stubs()
+            .will(returnValue(200));
+        ASSERT_EQ(100, mocker->method1(10));
+        ASSERT_EQ(200, mocker->method3(10));
+        mocker.reset(&Derived::method1);
+        ASSERT_THROWS_ANYTHING(mocker->method1(10));
+        ASSERT_EQ(200, mocker->method3(10));
+        MOCK_METHOD(mocker, method1)
+            .stubs()
+            .will(returnValue(1000));
+        ASSERT_EQ(1000, mocker->method1(10));
+        ASSERT_EQ(200, mocker->method3(10));
+        GlobalMockObject::verify();
+    }
+
+    TEST(can reset specified pure virtual function in same mock object)
+    {
+        MockObject<Derived> mocker;
+        MOCK_METHOD(mocker, method1)
+            .stubs()
+            .will(returnValue(100));
+        MOCK_METHOD(mocker, method3)
+            .stubs()
+            .will(returnValue(200));
+        ASSERT_EQ(100, mocker->method1(10));
+        ASSERT_EQ(200, mocker->method3(10));
+        mocker.reset(&Derived::method3);
+        ASSERT_EQ(100, mocker->method1(10));
+        ASSERT_THROWS_ANYTHING(mocker->method3(10));
+        MOCK_METHOD(mocker, method3)
+            .stubs()
+            .will(returnValue(2000));
+        ASSERT_EQ(100, mocker->method1(10));
+        ASSERT_EQ(2000, mocker->method3(10));
+        GlobalMockObject::verify();
+    }
+
 #if 0
     // if not use MOCKCPP_USE_MOCKABLE, compile error
     // if use MOCKCPP_USE_MOCKABLE, compile pass, but maybe all the MOCKER tests failed.
Index: tests/ut/TestMockObject.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMockObject.h b/tests/ut/TestMockObject.h
--- a/tests/ut/TestMockObject.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestMockObject.h	(date 1730193411332)
@@ -209,8 +209,9 @@
        mock.method(&Interface::base00).expects(once()).will(returnValue(20));
 
        TS_ASSERT_EQUALS(20, mock->base00());
+       mock->base00();
 
-       TS_ASSERT_THROWS(mock->base00(), Exception);
+       TS_ASSERT_THROWS(mock.verify(), Exception);
    }
 
    // once()
@@ -232,6 +233,7 @@
 
        TS_ASSERT_EQUALS(20, mock->base00());
        TS_ASSERT_EQUALS(20, mock->base00());
+       TS_ASSERT_THROWS_NOTHING(mock.verify());
    }
 
    // exactly()
@@ -271,6 +273,7 @@
        TS_ASSERT_EQUALS(20, mock->base00());
        TS_ASSERT_EQUALS(20, mock->base00());
        TS_ASSERT_EQUALS(20, mock->base00());
+       TS_ASSERT_THROWS_NOTHING(mock.verify());
    }
 
    // atLeast()
@@ -294,6 +297,7 @@
 
        TS_ASSERT_EQUALS(20, mock->base00());
        TS_ASSERT_EQUALS(20, mock->base00());
+       TS_ASSERT_THROWS_NOTHING(mock.verify());
    }
 
    // atMost()
@@ -304,6 +308,7 @@
        mock.method(&Interface::base00).expects(atMost(2)).will(returnValue(20));
 
        TS_ASSERT_EQUALS(20, mock->base00());
+       TS_ASSERT_THROWS_NOTHING(mock.verify());
    }
 
    // atMost()
@@ -311,11 +316,13 @@
    {
        MockObject<Interface> mock;
 
-       mock.method(&Interface::base00).expects(atMost(2)).will(returnValue(20));
+       mock.method(&Interface::base00).expects(atMost(1)).will(returnValue(20));
 
        TS_ASSERT_EQUALS(20, mock->base00());
        TS_ASSERT_EQUALS(20, mock->base00());
-       TS_ASSERT_THROWS(((Interface*)mock)->base00(), Exception);
+       TS_ASSERT_EQUALS(20, mock->base00());
+
+       TS_ASSERT_THROWS(mock.verify(), Exception);
    }
 
    // never()
Index: src/JmpOnlyApiHook.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpOnlyApiHook.cpp b/src/JmpOnlyApiHook.cpp
--- a/src/JmpOnlyApiHook.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/JmpOnlyApiHook.cpp	(date 1730193451812)
@@ -32,7 +32,11 @@
               , const void* stub)
 		: m_jmpCode(api, stub)
         , m_originalData(0)
-        , m_api(api)
+#ifndef __arm__
+         , m_api(api)
+#else
+        , m_api(IS_USE_THUMB_INST_SET(api) ? CONVERT_THUMB_INST_SET_FUNC_POINTER(api) : api)
+#endif
    {
       startHook();
    }
@@ -68,6 +72,7 @@
    void changeCode(const void* data)
    {
       CodeModifier::modify(const_cast<void*>(m_api), data, m_jmpCode.getCodeSize());
+      m_jmpCode.flushCache();
    }
 
    /////////////////////////////////////////////////////
Index: src/ApiHook.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/ApiHook.cpp b/src/ApiHook.cpp
--- a/src/ApiHook.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/ApiHook.cpp	(date 1730193449068)
@@ -39,8 +39,8 @@
 };
 
 /////////////////////////////////////////////////////////////////
-ApiHook::ApiHook 
-              ( const void* api 
+ApiHook::ApiHook
+              ( const void* api
               , const void* stub )
 	: This(new ApiHookImpl(api, stub))
 {
Index: include/mockcpp/ChainableMockMethodContainer.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/ChainableMockMethodContainer.h b/include/mockcpp/ChainableMockMethodContainer.h
--- a/include/mockcpp/ChainableMockMethodContainer.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/ChainableMockMethodContainer.h	(date 1730193427088)
@@ -40,6 +40,7 @@
     InvocationMocker* findInvocationMocker(const std::string& id) const;
 
     void reset();
+    void reset(ChainableMockMethodKey* key);
     void verify();
 
 private:
Index: tests/ut/TestInterfaceInfo.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestInterfaceInfo.h b/tests/ut/TestInterfaceInfo.h
--- a/tests/ut/TestInterfaceInfo.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestInterfaceInfo.h	(date 1730193410111)
@@ -30,10 +30,12 @@
       TESTCPP_VERIFY_RESOURCE_CHECK_POINT(checkpoint);
    }
 
+#ifndef __clang__
    void testShouldBeAbleCalculateNumberOfVptr_SingleInheritance()
    {
       TS_ASSERT_EQUALS(1, getNumberOfVtbls<Derived0>());
    }
+#endif
 
    struct Derived2 : public Base0, public Base1 {};
 
@@ -50,18 +52,20 @@
       TS_ASSERT_EQUALS(sizeof(Derived4)/sizeof(void*), getNumberOfVtbls<Derived4>());
    }
 
+#ifndef __clang__
    struct Derived5 : public Derived2, public Derived0 {};
    void testShouldBeAbleToCalculateTheNumberOfVptr_DupInheritance()
    {
       TS_ASSERT_EQUALS(sizeof(Derived5)/sizeof(void*), getNumberOfVtbls<Derived5>());
    }
+#endif
  
+#if !(defined(_MSC_VER) || defined(__clang__))
    struct Derived6 : virtual public Derived2, virtual public Derived0 {};
    void testShouldBeThrowExcpetion_VirtualInheritance()
    {
-      #ifndef _MSC_VER
-	  //VC does not support
+	   //VC does not support
       TS_ASSERT_THROWS(getNumberOfVtbls<Derived6>(), MOCKCPP_NS::Exception);
-	  #endif
-   }
+   }
+#endif
 };
Index: tests/ut/CMakeLists.txt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt
--- a/tests/ut/CMakeLists.txt	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/CMakeLists.txt	(date 1730193411801)
@@ -47,7 +47,10 @@
   TestMockcppSample.h
   TestApiHookBase.h
   TestSmartPointerChecker.h
+  TestOutBoundP.h
   MockMethodSamples.h
+  TestMockClassPublic.h
+  TestMockMutlThread.h
 )
 
 IF(UNIX)
@@ -97,6 +100,7 @@
 ELSE(MSVC)
     ADD_DEFINITIONS(-std=c++14)
 ENDIF(MSVC)
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pmf-conversions")
 
 IF(UNIX OR MINGW)
 ADD_DEFINITIONS(
@@ -134,7 +138,8 @@
 
   TARGET_LINK_LIBRARIES(${CASE_MODULE}
     testngpp-static
-    mockcpp)
+    mockcpp
+    pthread)
 
   ADD_CUSTOM_COMMAND(
     OUTPUT ${CASE_SRC}
Index: tests/ut/TestMethodInfoReader.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestMethodInfoReader.h b/tests/ut/TestMethodInfoReader.h
--- a/tests/ut/TestMethodInfoReader.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestMethodInfoReader.h	(date 1730193412252)
@@ -126,6 +126,8 @@
       TS_ASSERT(getDeltaOfMethod(&Interface::base00) != getDeltaOfMethod(&Interface::base10));
    }
 
+// 在arm64平台下虚函数和普通函数的 m.desc.u.index 都是偶数,在 GnuMethodInfoReader.h 中做的特殊处理对此测试例不生效
+#ifndef __aarch64__
    void testShouldThrowAnExceptionIfTryToGetVtblIndexOfANonVirtualMethod()
    {
       TS_ASSERT_THROWS(getIndexOfMethod(&Interface::c), MOCKCPP_NS::Exception);
@@ -140,5 +142,6 @@
    {
       TS_ASSERT_THROWS(MOCKCPP_NS::getAddrOfMethod(&Interface::base00), MOCKCPP_NS::Exception);
    }
+#endif
 };
 
Index: src/VirtualTable.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/VirtualTable.cpp b/src/VirtualTable.cpp
--- a/src/VirtualTable.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/VirtualTable.cpp	(date 1730193449973)
@@ -54,6 +54,7 @@
 
    FakeObject* fakeObject;
    void** vtbl;
+   void** vtblBak;
    unsigned int numberOfVptr;
    IndexInvokableGetter* indexInvokableGetter;
    ObjectNameGetter* nameGetter;
@@ -208,6 +209,8 @@
 
    initializeVtbls(vptr, vtbl, numberOfVptr,refTypeInfo, true);
 
+   vtblBak = createVtbls(numberOfVptr);
+
    vptr[MOCKCPP_MAX_INHERITANCE] = (void*)this;
 
    deleted = false;
@@ -228,6 +231,12 @@
        freeVtbls(vtbl, numberOfVptr);
        vtbl = 0;
     }
+
+    if (vtblBak != nullptr)
+    {
+       freeVtbls(vtblBak, numberOfVptr);
+       vtblBak = nullptr;
+    }
 }
 
 /////////////////////////////////////////////////////////////////
@@ -255,12 +264,15 @@
 }
  
 /////////////////////////////////////////////////////////////////
-void
-VirtualTable::addMethod(void* methodAddr, unsigned int indexOfVtbl, unsigned int indexOfVptr)
+void VirtualTable::
+addMethod(void* methodAddr, unsigned int indexOfVtbl, unsigned int indexOfVptr)
 {
     This->validateIndexOfVtbl(indexOfVtbl);
     This->validateIndexOfVptr(indexOfVptr);
 
+    if (This->vtblBak[getRealVtblIndex(indexOfVptr, indexOfVtbl)] == nullptr)
+        This->vtblBak[getRealVtblIndex(indexOfVptr, indexOfVtbl)] =
+            This->vtbl[getRealVtblIndex(indexOfVptr, indexOfVtbl)];
     This->vtbl[getRealVtblIndex(indexOfVptr, indexOfVtbl)] = methodAddr;
 }
 
@@ -322,6 +334,18 @@
     This->reset();
 }
 
+void
+VirtualTable::reset(unsigned int vptrIndex, unsigned int vtblIndex)
+{
+    if (This->vtblBak[getRealVtblIndex(vptrIndex, vtblIndex)] == nullptr)
+        return;
+    This->validateIndexOfVptr(vptrIndex);
+    This->validateIndexOfVtbl(vtblIndex);
+    This->vtbl[getRealVtblIndex(vptrIndex, vtblIndex)] =
+        This->vtblBak[getRealVtblIndex(vptrIndex, vtblIndex)];
+    This->vtblBak[getRealVtblIndex(vptrIndex, vtblIndex)] = nullptr;
+}
+
 /////////////////////////////////////////////////////////////////
 void
 VirtualTable::verify()
Index: tests/ut/TestApiHook.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestApiHook.h b/tests/ut/TestApiHook.h
--- a/tests/ut/TestApiHook.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestApiHook.h	(date 1730193410011)
@@ -39,6 +39,16 @@
     return 0;
 }
 
+int bar(void)
+{
+	return 0;
+}
+
+int baz(int a, int b)
+{
+	return 0;
+}
+
 FIXTURE(ApiHook)
 {
 	int a; //TODO: static const cause linux .so load failure.
@@ -90,10 +100,70 @@
 		ASSERT_EQ(20, func2(500));
 	}
 
-        TEST(can mock 2 functions which has same prototype)
-        {
-               MOCKER(foo).stubs().will(returnValue(20));
-               ASSERT_EQ(ret, func(a, b));
-               ASSERT_EQ(20,  foo(a, b));
-        }
+	TEST(can mock 2 functions which has same prototype)
+	{
+		MOCKER(foo).stubs().will(returnValue(20));
+		ASSERT_EQ(ret, func(a, b));
+		ASSERT_EQ(20,  foo(a, b));
+	}
+
+	TEST(can reset specified function for different prototype 1)
+	{
+		func(a, b);
+		MOCKER(foo).stubs().will(returnValue(10));
+		MOCKER(bar).stubs().will(returnValue(20));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(20,  bar());
+		GlobalMockObject::reset((const void *)foo);
+		ASSERT_EQ(0,  foo(a, b));
+		ASSERT_EQ(20,  bar());
+		MOCKER(foo).stubs().will(returnValue(100));
+		ASSERT_EQ(100,  foo(a, b));
+		ASSERT_EQ(20,  bar());
+	}
+
+	TEST(can reset specified function for different prototype 2)
+	{
+		func(a, b);
+		MOCKER(foo).stubs().will(returnValue(10));
+		MOCKER(bar).stubs().will(returnValue(20));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(20,  bar());
+		GlobalMockObject::reset((const void *)bar);
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(0,  bar());
+		MOCKER(bar).stubs().will(returnValue(200));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(200,  bar());
+	}
+
+	TEST(can reset specified function for same prototype 1)
+	{
+		func(a, b);
+		MOCKER(foo).stubs().will(returnValue(10));
+		MOCKER(baz).stubs().will(returnValue(20));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(20,  baz(a, b));
+		GlobalMockObject::reset((const void *)foo);
+		ASSERT_EQ(0,  foo(a, b));
+		ASSERT_EQ(20,  baz(a, b));
+		MOCKER(foo).stubs().will(returnValue(100));
+		ASSERT_EQ(100,  foo(a, b));
+		ASSERT_EQ(20,  baz(a, b));
+	}
+
+	TEST(can reset specified function for same prototype 2)
+	{
+		func(a, b);
+		MOCKER(foo).stubs().will(returnValue(10));
+		MOCKER(baz).stubs().will(returnValue(20));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(20,  baz(a, b));
+		GlobalMockObject::reset((const void *)baz);
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(0,  baz(a, b));
+		MOCKER(baz).stubs().will(returnValue(200));
+		ASSERT_EQ(10,  foo(a, b));
+		ASSERT_EQ(200,  baz(a, b));
+	}
 };
Index: tests/3rdparty/testngpp/src/mem_checker/debug_new.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/3rdparty/testngpp/src/mem_checker/debug_new.cpp b/tests/3rdparty/testngpp/src/mem_checker/debug_new.cpp
--- a/tests/3rdparty/testngpp/src/mem_checker/debug_new.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/3rdparty/testngpp/src/mem_checker/debug_new.cpp	(date 1730193408115)
@@ -54,6 +54,7 @@
 #include <mem_checker/reporter.h>
 #include <mem_checker/format.h>
 #include <mem_checker/check_status.h>
+#include <mutex>
 
 
 
@@ -260,12 +261,12 @@
 /**
  * The mutex guard to protect simultaneous access to the pointer list.
  */
-static fast_mutex new_ptr_lock;
+static std::mutex new_ptr_lock;
 
 /**
  * The mutex guard to protect simultaneous output to #new_output_fp.
  */
-static fast_mutex new_output_lock;
+static std::mutex new_output_lock;
 
 /**
  * Total memory allocated in bytes.
@@ -507,7 +508,7 @@
 #if _DEBUG_NEW_STD_OPER_NEW
         return NULL;
 #else
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         #ifndef USED_IN_XUNIT
         fprintf(new_output_fp,
                 "Out of memory when allocating %u bytes\n",
@@ -538,7 +539,7 @@
     ptr->magic = MAGIC;
     ptr->check_status = getCheckStatus();
     {
-        fast_mutex_autolock lock(new_ptr_lock);
+        std::lock_guard<std::mutex> lock_ptr(new_ptr_lock);
         ptr->prev = new_ptr_list.prev;
         ptr->next = &new_ptr_list;
         new_ptr_list.prev->next = ptr;
@@ -550,7 +551,7 @@
 #endif
     if (new_verbose_flag)
     {
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         fprintf(new_output_fp,
                 "new%s: allocated %p (size %lu, ",
                 is_array ? "[]" : "",
@@ -583,7 +584,7 @@
     if (ptr->magic != MAGIC)
     {
         {
-            fast_mutex_autolock lock(new_output_lock);
+            std::lock_guard<std::mutex> lock(new_output_lock);
             #ifndef USED_IN_XUNIT
             fprintf(new_output_fp, "delete%s: invalid pointer %p (",
                     is_array ? "[]" : "", pointer);
@@ -611,7 +612,7 @@
             msg = "delete[] after new";
         else
             msg = "delete after new[]";
-        fast_mutex_autolock lock(new_output_lock);        
+        std::lock_guard<std::mutex> lock(new_output_lock);        
 #ifndef USED_IN_XUNIT
         fprintf(new_output_fp,
                 "%s: pointer %p (%u bytes)\n\tat ",
@@ -648,7 +649,7 @@
     }
 #endif
     {
-        fast_mutex_autolock lock(new_ptr_lock);
+        std::lock_guard<std::mutex> lock_ptr(new_ptr_lock);
         total_mem_alloc -= ptr->size;
         ptr->magic = 0;
         ptr->prev->next = ptr->next;
@@ -656,7 +657,7 @@
     }
     if (new_verbose_flag)
     {
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         fprintf(new_output_fp,
                 "delete%s: freed %p (size %lu, %lu bytes still allocated)\n",
                 is_array ? "[]" : "",
@@ -686,7 +687,7 @@
 
 void clear_all_allocate_info()
 {
-    fast_mutex_autolock lock_ptr(new_ptr_lock);
+    std::lock_guard<std::mutex> lock_ptr(new_ptr_lock);
     new_ptr_list_t* ptr = new_ptr_list.next;
     while (ptr != &new_ptr_list)
     {
@@ -706,8 +707,8 @@
 {
     int leak_cnt = 0;
     unsigned int print_count_this_time = 0;
-    fast_mutex_autolock lock_ptr(new_ptr_lock);
-    fast_mutex_autolock lock_output(new_output_lock);
+    std::lock_guard<std::mutex> lock_ptr(new_ptr_lock);
+    std::lock_guard<std::mutex> lock_output(new_output_lock);
     new_ptr_list_t* ptr = new_ptr_list.next;
     while (ptr != &new_ptr_list)
     {
@@ -784,8 +785,8 @@
 int check_mem_corruption()
 {
     int corrupt_cnt = 0;
-    fast_mutex_autolock lock_ptr(new_ptr_lock);
-    fast_mutex_autolock lock_output(new_output_lock);
+    std::lock_guard<std::mutex> lock_ptr(new_ptr_lock);
+    std::lock_guard<std::mutex> lock_output(new_output_lock);
     fprintf(new_output_fp, "*** Checking for memory corruption: START\n");
     for (new_ptr_list_t* ptr = new_ptr_list.next;
             ptr != &new_ptr_list;
@@ -839,7 +840,7 @@
             (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE);
     if (ptr->magic != MAGIC || ptr->line != 0)
     {
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         get_info_reporter()->report(
 		        _M_file, _M_line,
                 "warning: debug_new used with placement new."
@@ -917,7 +918,7 @@
 {
     if (new_verbose_flag)
     {
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         fprintf(new_output_fp,
                 "info: exception thrown on initializing object at %p (",
                 pointer);
@@ -931,7 +932,7 @@
 {
     if (new_verbose_flag)
     {
-        fast_mutex_autolock lock(new_output_lock);
+        std::lock_guard<std::mutex> lock(new_output_lock);
         fprintf(new_output_fp,
                 "info: exception thrown on initializing objects at %p (",
                 pointer);
Index: include/mockcpp/MockObjectBase.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/MockObjectBase.h b/include/mockcpp/MockObjectBase.h
--- a/include/mockcpp/MockObjectBase.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/MockObjectBase.h	(date 1730193427548)
@@ -52,6 +52,7 @@
    void* toPointerToInterface() const;
 
    void setDestructor(unsigned int vptrIndex, unsigned int vtblIndex);
+   void resetVirtualMethod(unsigned int vptrIndex, unsigned int vtblIndex);
 
    void expectsBeingDeleted();
    void expectsKeepAlive();
Index: src/InvokedAtMost.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/InvokedAtMost.cpp b/src/InvokedAtMost.cpp
--- a/src/InvokedAtMost.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/InvokedAtMost.cpp	(date 1730193453873)
@@ -36,13 +36,6 @@
 ///////////////////////////////////////////////////////
 void InvokedAtMost::increaseInvoked(const Invocation& inv)
 {
-    oss_t oss;
-
-    oss << "Expected at most " << highLimit 
-        << " times, but you are trying to invoke more than that.";
-
-    MOCKCPP_ASSERT_TRUE_MESSAGE(
-         oss.str(), getInvokedTimes() < highLimit);
 }
 
 ///////////////////////////////////////////////////////
@@ -58,17 +51,14 @@
 ///////////////////////////////////////////////////////
 void InvokedAtMost::verify(void)
 {
-// We won't need to verify it here, it was checked at runtime.
-#if 0
     oss_t oss;
-    
-    oss << "Expected at most " << highLimit 
+
+    oss << "Expected at most " << highLimit
         << " times, but it's actually invoked " << getInvokedTimes() << " times";
 
     MOCKCPP_ASSERT_TRUE_MESSAGE(
 			oss.str(),
          getInvokedTimes() <= highLimit);
-#endif
 }
 
 MOCKCPP_NS_END

Index: include/mockcpp/MockObject.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/MockObject.h b/include/mockcpp/MockObject.h
--- a/include/mockcpp/MockObject.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/MockObject.h	(date 1730193426128)
@@ -123,6 +123,14 @@
       MockObjectBase::reset();
       identifyDestructor<Interface, Interface>();
    }
+
+	template <typename Method>
+   void reset(Method m)
+   {
+      std::pair<unsigned int, unsigned int> indices = \
+         getIndicesOfMethod<Interface, Method>(m);
+      resetVirtualMethod(indices.first, indices.second);
+   }
    /////////////////////////////////////////////////////////////
 	template <typename Method>
    InvocationMockBuilderGetter method(Method m, const char* name = 0)
Index: tests/ut/TestFormatter.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestFormatter.h b/tests/ut/TestFormatter.h
--- a/tests/ut/TestFormatter.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestFormatter.h	(date 1730193411704)
@@ -301,12 +301,15 @@
       TS_ASSERT_EQUALS(expected, toTypeAndValueString(c));
    }
 
+// 在arm64平台下-1的返回值是 (std::string)"(char)0xff/255" 与期望值 (std::string)"(char)0xff/-1" 不符
+#ifndef __aarch64__
    void testShouldBeAbleToStringnizeNegativeChar()
    {
       char c = -1;
       std::string expected("(char)0xff/-1");
       TS_ASSERT_EQUALS(expected, toTypeAndValueString(c));
    }
+#endif
 
    void testShouldBeAbleToStringnizeShort()
    {
Index: tests/ut/TestCheck.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestCheck.h b/tests/ut/TestCheck.h
--- a/tests/ut/TestCheck.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestCheck.h	(date 1730193409166)
@@ -139,5 +139,34 @@
 
 		ASSERT_EQ(10, input.a);
 	}
+
+	TEST(Can check with mutable lambda)
+	{
+		MOCK_METHOD(mocker, method)
+			.expects(once())
+			.with(checkWith([](STRUCT_T *p) mutable {
+				p->a = 10;
+				return p->b == 2;
+			}));
+
+		client(mocker, &input);
+
+		ASSERT_EQ(10, input.a);
+	}
+
+	TEST(Can check with immutable lambda)
+	{
+		MOCK_METHOD(mocker, method)
+			.expects(once())
+			.with(checkWith([](STRUCT_T *p) {
+				p->a = 10;
+				return p->b == 2;
+			}));
+
+		client(mocker, &input);
+
+		ASSERT_EQ(10, input.a);
+	}
+
 };
 
Index: src/InvokedOnce.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/InvokedOnce.cpp b/src/InvokedOnce.cpp
--- a/src/InvokedOnce.cpp	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/InvokedOnce.cpp	(date 1730193448285)
@@ -35,14 +35,6 @@
 ///////////////////////////////////////////////////////
 void InvokedOnce::increaseInvoked(const Invocation& inv)
 {
-    oss_t oss;
-
-    oss << "Invocation is expected only once(), but you are trying to "
-        << "invoke more than that";
-
-    MOCKCPP_ASSERT_TRUE_MESSAGE(
-         oss.str(), getInvokedTimes() < 1);
-   
 }
 ///////////////////////////////////////////////////////
 std::string
Index: src/JmpCodeX86.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCodeX86.h b/src/JmpCodeX86.h
--- a/src/JmpCodeX86.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/src/JmpCodeX86.h	(date 1730193447584)
@@ -23,5 +23,7 @@
             (unsigned long long)to - (unsigned long long)from - sizeof(jmpCodeTemplate); \
    } while(0)
 
+#define FLUSH_CACHE(from, length) do {} while (0)
+
 #endif
 
Index: tests/ut/TestStaticMethodMocker.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/tests/ut/TestStaticMethodMocker.h b/tests/ut/TestStaticMethodMocker.h
--- a/tests/ut/TestStaticMethodMocker.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/tests/ut/TestStaticMethodMocker.h	(date 1730193410647)
@@ -32,6 +32,7 @@
         }
     };
 
+#if !(defined(__aarch64__) && defined(__clang__))
     TEST(static member function mocker test)
     {
         MOCKER(CUT::func)
@@ -40,4 +41,5 @@
         ASSERT_EQ(100, CUT::func());
         GlobalMockObject::verify();
     }
+#endif
 };

Index: src/JmpCodeAArch64.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCodeAArch64.h b/src/JmpCodeAArch64.h
new file mode 100644
--- /dev/null	(date 1730193450247)
+++ b/src/JmpCodeAArch64.h	(date 1730193450247)
@@ -0,0 +1,82 @@
+
+#ifndef __MOCKCPP_JMP_CODE_AARCH64_H__
+#define __MOCKCPP_JMP_CODE_AARCH64_H__
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+MOCKCPP_NS_START
+
+struct l2cache_addr_range {
+    uintptr_t start;
+    uintptr_t end;
+};
+
+MOCKCPP_NS_END
+
+#define ADDR_ALIGN_UP(addr) ((((addr) + ((4096) - 1)) & (~((4096) - 1))) & 0xffffffffffffffff)
+#define ADDR_ALIGN_DOWN(addr) (((addr) & (~((4096) - 1))) & 0xffffffffffffffff)
+
+const unsigned char jmpCodeTemplate[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                         0x00, 0x00, 0x00, 0x00};
+
+#define ARM_BYTE_ORDER(x)   ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24)
+// br x9 跳转到桩函数,执行后直接从桩函数返回,回到`x30`存储的位置。因为在这`5`条指令中未修改`x30`,所以回到了调用者函数的位置
+const uint32_t jump_code = ARM_BYTE_ORDER(0x20011fd6);
+
+static inline uint32_t get_mov_xn_code(uintptr_t val, uint32_t reg_id)
+{
+    uint32_t code = 0xd2800000; // mov 指令是 11010010 100 + 16bit val + 5bit reg id
+    val = (val & 0xffff) << 5;
+    code |= (uint32_t)val;
+    reg_id &= 0x1f;
+    code |= reg_id;
+    return code;
+}
+
+static inline uint32_t get_movk_xn_code(uintptr_t val, uint32_t pos, uint32_t reg_id)
+{
+    uint32_t code = 0xf2800000; // movk 指令是 11110010 1 + 2bit offset + 16bit val + 5bit reg id
+    pos &= 0x3;
+    val = ((val >> (16 * pos)) & 0xffff) << 5;
+    code |= (uint32_t)val;
+    pos = pos << 21;
+    code |= pos;
+    reg_id &= 0x1f;
+    code |= reg_id;
+    return code;
+}
+
+#define SET_JMP_CODE(base, from, to) do {                       \
+    uint32_t reg = 9;                                           \
+    uint32_t d_index = 0;                                       \
+    uint32_t *d_ptr = (uint32_t *)base;                         \
+    d_ptr[d_index++] = get_mov_xn_code((uintptr_t)to, reg);     \
+    d_ptr[d_index++] = get_movk_xn_code((uintptr_t)to, 1, reg); \
+    d_ptr[d_index++] = get_movk_xn_code((uintptr_t)to, 2, reg); \
+    d_ptr[d_index++] = get_movk_xn_code((uintptr_t)to, 3, reg); \
+    d_ptr[d_index++] = jump_code;                               \
+} while (0)
+
+#ifdef __GNUC__
+#define FLUSH_CACHE(from, length) do {                                      \
+    struct l2cache_addr_range usr_data;                                     \
+    usr_data.start = ADDR_ALIGN_DOWN((unsigned long long)from);             \
+    usr_data.end = ADDR_ALIGN_UP((unsigned long long)from) + length;        \
+    __builtin___clear_cache((char *)usr_data.start, (char *)usr_data.end);  \
+} while (0)
+#else
+#define FLUSH_CACHE(from, length) do {} while (0)
+#endif
+
+#endif
Index: src/JmpCodeAArch32.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/JmpCodeAArch32.h b/src/JmpCodeAArch32.h
new file mode 100644
--- /dev/null	(date 1730193451052)
+++ b/src/JmpCodeAArch32.h	(date 1730193451052)
@@ -0,0 +1,158 @@
+
+#ifndef __MOCKCPP_JMP_CODE_AARCH32_H__
+#define __MOCKCPP_JMP_CODE_AARCH32_H__
+
+#include <mockcpp/mockcpp.h>
+
+MOCKCPP_NS_START
+
+struct l2cache_addr_range {
+    uintptr_t start;
+    uintptr_t end;
+};
+
+MOCKCPP_NS_END
+
+#define ADDR_ALIGN_UP(addr) ((((addr) + ((4096) - 1)) & (~((4096) - 1))) & 0xffffffff)
+#define ADDR_ALIGN_DOWN(addr) (((addr) & (~((4096) - 1))) & 0xffffffff)
+
+#define IS_USE_THUMB_INST_SET(ptr) (((uintptr_t)(ptr) % 2) != 0)
+#define CONVERT_THUMB_INST_SET_FUNC_POINTER(ptr) ((void *)((uintptr_t)ptr - 1))
+
+/*
+ * 参考 《DDI0406C_d_armv7ar_arm.pdf》 A8.8.103 MOV (immediate)
+ * thumb MOVW encoding T3的编码方式如下:
+ * 11110 i 100100 imm4 0 imm3 Rd imm8
+ * arm MOVW encoding A2的编码方式如下:
+ * cond 00110000 imm4 Rd imm12
+ */
+static uint32_t get_thumb_movw_imme_code(uint8_t reg_id, uint16_t imme)
+{
+    uint32_t code = 0xf2400000;
+    code |= ((reg_id & 0xff) << 8);
+    code |= (((imme & 0xf000) >> 12) << 16);
+    code |= ((((imme & 0x0f00) >> 8) & 0b0111) << 12);
+    code |= (((((imme & 0x0f00) >> 8) & 0b1000) >> 3) << 26);
+    code |= ((imme & 0x00ff));
+    return code;
+}
+
+static uint32_t get_arm32_movw_imme_code(uint8_t reg_id, uint16_t imme)
+{
+    uint32_t code = 0xE3000000;
+    code |= ((reg_id & 0xff) << 12);
+    code |= (((imme & 0xf000) >> 12) << 16);
+    code |= (imme & 0x0fff);
+    return code;
+}
+
+/*
+ * 参考 《DDI0406C_d_armv7ar_arm.pdf》 A8.8.107 MOVT
+ * thumb MOVT encoding T1的编码方式如下:
+ * 11110 i 101100 imm4 0 imm3 Rd imm8
+ * arm MOVT encoding A1的编码方式如下:
+ * cond 00110100 imm4 Rd imm12
+ */
+static uint32_t get_thumb_movt_imme_code(uint8_t reg_id, uint16_t imme)
+{
+    uint32_t code = 0xf2c00000;
+    code |= ((reg_id & 0xff) << 8);
+    code |= (((imme & 0xf000) >> 12) << 16);
+    code |= ((((imme & 0x0f00) >> 8) & 0b0111) << 12);
+    code |= (((((imme & 0x0f00) >> 8) & 0b1000) >> 3) << 26);
+    code |= ((imme & 0x00ff));
+    return code;
+}
+
+static uint32_t get_arm32_movt_imme_code(uint8_t reg_id, uint16_t imme)
+{
+    uint32_t code = 0xE3400000;
+    code |= ((reg_id & 0xff) << 12);
+    code |= (((imme & 0xf000) >> 12) << 16);
+    code |= (imme & 0x0fff);
+    return code;
+}
+
+/*
+ * 参考 《DDI0406C_d_armv7ar_arm.pdf》 A8.8.27 BX
+ * thumb BX encoding T1的编码方式如下:
+ * 010001110 Rm 000
+ * arm BX encoding A1的编码方式如下:
+ * cond 00010010111111111111 +0001 Rm
+ */
+static uint16_t get_thumb_bx_code(uint8_t reg_id)
+{
+    uint16_t code = 0x4700;
+    code |= (reg_id & 0xff) << 3;
+    return code;
+}
+
+static uint32_t get_arm32_bx_code(uint8_t reg_id)
+{
+    uint32_t code = 0xE12FFF10;
+    code |= (reg_id & 0xff);
+    return code;
+}
+
+/*
+ * 不改变任何寄存器和栈空间的跳转指令组合:
+ *
+ * thumb模式下的跳转指令需要6条指令22个字节, arm32模式下的跳转指令需要6条指令24个字节:
+ * sub	sp, #8                # 申请8 bytes的栈空间
+ * str.w	r9, [sp]          # 将R9保存在栈顶向下的4 bytes(32位中)
+ * mov.w	r9, stub_addr_low #
+ * movt	r9, stub_addr_high    # 将stub_addr保存在r9寄存器中
+ * str.w	r9, [sp, #4]      # 将R9保存在剩下的4 bytes(32位中)栈空间中
+ * pop      {r9, pc}          # 将栈内的8bytes弹出, 同时赋值给r9和pc寄存器
+ */
+
+static constexpr uint16_t SUB_SP_8_THUMB_CODE = 0xB082;
+static constexpr uint32_t STR_R9_SP_THUMB_CODE = 0xF8CD9000;
+static constexpr uint32_t STR_R9_SP_4_THUMB_CODE = 0xF8CD9004;
+static constexpr uint32_t POP_R9_PC_THUMB_CODE = 0xE8BD8200;
+
+static constexpr uint32_t SUB_SP_8_ARM_CODE = 0xE24DD008;
+static constexpr uint32_t STR_R9_SP_ARM_CODE = 0xE58D9000;
+static constexpr uint32_t STR_R9_SP_4_ARM_CODE = 0xE58D9004;
+static constexpr uint32_t POP_R9_PC_ARM_CODE = 0xE8BD8200;
+
+const unsigned char jmpCodeTemplate[24]  = { 0 };
+#define SET_JMP_CODE(base, from, to) do { \
+    uint32_t stub_addr = (uint32_t)to; \
+    char *target_addr = (char *)base; \
+    if (IS_USE_THUMB_INST_SET(from)) { \
+        uint32_t thumbMovwCode = get_thumb_movw_imme_code(9, (stub_addr & 0xffff)); \
+        uint32_t thumbMovtCode = get_thumb_movt_imme_code(9, (stub_addr >> 16)); \
+        ((uint16_t*)target_addr)[0] = SUB_SP_8_THUMB_CODE; \
+        ((uint16_t*)target_addr)[1] = (STR_R9_SP_THUMB_CODE >> 16); \
+        ((uint16_t*)target_addr)[2] = (STR_R9_SP_THUMB_CODE & 0xffff); \
+        ((uint16_t*)target_addr)[3] = (thumbMovwCode >> 16); \
+        ((uint16_t*)target_addr)[4] = (thumbMovwCode & 0xffff); \
+        ((uint16_t*)target_addr)[5] = (thumbMovtCode >> 16); \
+        ((uint16_t*)target_addr)[6] = (thumbMovtCode & 0xffff); \
+        ((uint16_t*)target_addr)[7] = (STR_R9_SP_4_THUMB_CODE >> 16); \
+        ((uint16_t*)target_addr)[8] = (STR_R9_SP_4_THUMB_CODE & 0xffff); \
+        ((uint16_t*)target_addr)[9] = (POP_R9_PC_THUMB_CODE >> 16); \
+        ((uint16_t*)target_addr)[10] = (POP_R9_PC_THUMB_CODE & 0xffff); \
+    } else { \
+        ((uint32_t*)target_addr)[0] = SUB_SP_8_ARM_CODE; \
+        ((uint32_t*)target_addr)[1] = STR_R9_SP_ARM_CODE; \
+        ((uint32_t*)target_addr)[2] = get_arm32_movw_imme_code(9, (stub_addr & 0xffff)); \
+        ((uint32_t*)target_addr)[3] = get_arm32_movt_imme_code(9, (stub_addr >> 16)); \
+        ((uint32_t*)target_addr)[4] = STR_R9_SP_4_ARM_CODE; \
+        ((uint32_t*)target_addr)[5] = POP_R9_PC_ARM_CODE; \
+    } \
+} while(0)
+
+#ifdef __GNUC__
+#define FLUSH_CACHE(from, length) do {                                      \
+    struct l2cache_addr_range usr_data;                                     \
+    usr_data.start = ADDR_ALIGN_DOWN((unsigned long long)from);             \
+    usr_data.end = ADDR_ALIGN_UP((unsigned long long)from) + length;        \
+    __builtin___clear_cache((char *)usr_data.start, (char *)usr_data.end);  \
+} while (0)
+#else
+#define FLUSH_CACHE(from, length) do {} while (0)
+#endif
+
+#endif
Index: include/mockcpp/GlobalMockObject.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/include/mockcpp/GlobalMockObject.h b/include/mockcpp/GlobalMockObject.h
--- a/include/mockcpp/GlobalMockObject.h	(revision e8d8b8fa25830b7f6b94281c5c5aee67ee87836f)
+++ b/include/mockcpp/GlobalMockObject.h	(date 1730193416997)
@@ -38,6 +38,7 @@
 {
    static void verify();
    static void reset();
+   static void reset(const void* api);
 
    static MockObjectType instance;
 };