使用C++对java的classloader进行模拟

来源:互联网 发布:淘宝海报制作软件 编辑:程序博客网 时间:2024/05/16 23:40

转自:使用C++对java的classloader进行模拟

 

一直以来,觉得java的classloader很不错的,做产品的话,可以将基本的做下来后,将扩展通过classloader的方式来做,将更新的补丁使用classloader来做,在使用java的网络游戏中,可以将扩展通过classloader的机制,实现动态的更新,省的每次更新要重新下载客户端,在手机上可是一个很不错的点子。
erlang也支持代码的热部署,java也可以动态更新,那么,C++呢,思来考去,没有想到类似非常完美的方案,只能知道一个基本成型的内容。

现在,不管是windows还是linux,都支持dll,我们可以动态的加载dll,是否可以像java那样,动态的调用函数呢?答案是可以的。需要借助部分汇编来实现,要知道,我们可能只是知道一个函数的名字,函数参数的类型,参数的个数等等,都是不知道的。因为我更想做一个普遍使用的,而不是要告诉你:“把某某的头文件给我”。
首先看外表的内容,java的ClassLoader的模拟接口:
ClassLoader.h


01       #ifndef _CLASSLOADER_H_
02#define _CLASSLOADER_H_
03#include "define.h"
04#include "String.h"
05class ClassLoader
06{
07public:
08        ClassLoader();
09        ClassLoader(ClassLoader* parent);
10        virtual ~ClassLoader();
11        void load(const String& name);
12        void* getFun(const String& name);
13private:
14        ClassLoader* parent;
15        void* dl_handle;
16};
17#endif /* _CLASSLOADER_H_ */

接下来是ClassLoader.cpp

view source
print?
01       #include "java/lang/ClassLoader.h"
02#include < dlfcn.h >
03#include "System.h"
04ClassLoader::ClassLoader()
05{
06        dl_handle = 0;
07}
08 
09ClassLoader::~ClassLoader()
10{
11        if (dl_handle) {
12                dlclose(dl_handle);
13        }
14        dl_handle = 0;
15}
16 
17ClassLoader::ClassLoader(ClassLoader* parent)
18{
19        dl_handle = 0;
20        this->parent = parent;
21}
22void ClassLoader::load(const String& name)
23{
24        System::out::printf("ClassLoader::load %s/n", (char*)name);
25        dl_handle = dlopen((char*)name, RTLD_LAZY);
26        if (!dl_handle) {
27                 System::out::printf("!!! %s/n", dlerror());
28        }
29}
30 
31void* ClassLoader::getFun(const String& name)
32{
33        System::out::printf("ClassLoader::getFun %s/n", name.c_str());
34        void* fun = dlsym(dl_handle, name.c_str());
35        char* error = dlerror();
36        if (error != 0) {
37                System::out::printf("error %s/n", error);
38                return 0;
39        }else
40                return fun;
41}

java的Class的封装:
Class.h


01       #ifndef _CLASS_H_
02#define _CLASS_H_
03#include "define.h"
04#include "String.h"
05class Object;
06class Method;
07class ClassLoader;
08class Class
09{
10public:
11        Class();
12        Class(const Class& aClass);
13        virtual ~Class();
14        Object* newInstance();
15        static Class forName(const String& name, boolean initialize,   /
16                              ClassLoader* loader);
17        Method* getMethod(const String& name, void* parm);
18        ClassLoader* loader;
19        String name;
20        boolean initialize;
21};
22#endif /* _CLASS_H_ */

java的Class的封装实现:
Class.cpp


01       #include "java/lang/Class.h"
02#include "java/lang/Object.h"
03#include "java/lang/reflect/Method.h"
04#include "java/lang/ClassLoader.h"
05#include "System.h"
06#include < dlfcn.h >
07Class::Class()
08{
09        loader = 0;
10        initialize = false;
11}
12 
13Class::Class(const Class& aClass)
14{
15        this->name = aClass.name;
16        this->loader = aClass.loader;
17        this->initialize = aClass.initialize;
18}
19Class::~Class()
20{
21 
22}
23 
24Class Class::forName(const String& name, boolean initialize,   /
25                      ClassLoader* loader)
26{
27        Class ret;
28        ret.name = name;
29        ret.initialize = initialize;
30        ret.loader = loader;
31        if (ret.loader != 0) {
32                ret.loader->load(name);
33        }
34        return ret;
35}
36 
37Object* Class::newInstance()
38{
39        System::out::println(this->name);
40        Object* ret = new Object();
41        ret->c.name = this->name;
42        ret->c.initialize = this->initialize;
43        ret->c.loader = this->loader;
44        return ret;
45}
46 
47Method* Class::getMethod(const String& name, void* parm)
48{
49        System::out::println("Class::getMethod");
50        System::out::println(this->name);
51        System::out::println(name);
52        return new Method(this->loader->getFun(name));
53}

Java的函数Method的封装:
Method.h


01       #ifndef _METHOD_H_
02#define _METHOD_H_
03 
04#include "String.h"
05class Object;
06class Method
07{
08public:
09        Method();
10        virtual ~Method();
11        Method(void* fun);
12        void invoke(Object* obj, ... );
13        void* fun;
14};
15 
16#endif /* _METHOD_H_ */

上面的内容,没什么好说的,linux下编译,理论上,windows也是可以的,只是调用的函数不一样而已。核心的实现在Method的实现中,毕竟,我们做了这么多,就是为了调用函数。这是实现:
Method.cpp


01       #include "java/lang/reflect/Method.h"
02#include "java/lang/Object.h"
03#include "stdarg.h"
04Method::Method()
05{
06        this->fun = 0;
07}
08 
09Method::~Method()
10{
11 
12}
13 
14void Method::invoke(Object* obj, ...)
15{
16        if (this->fun == 0)
17                return;
18        typedef void (*func)(void* ...);
19        func fun;
20        va_list ap;
21        int inc = 0;
22        va_start(ap, obj);
23        void** arg = 0;
24        while(true) {
25                void* s = va_arg(ap,void*);
26                if (s == 0)
27                        break;
28                inc+=4;
29        }
30        int argc = inc/4;
31        arg = new void*[argc];
32        va_start(ap, obj);
33        while(true) {
34                void* s = va_arg(ap,void*);
35                if (s == 0)
36                        break;
37                argc--;
38                arg[argc] = s;
39        }
40        va_start(ap, obj);
41        while (true) {
42                void* s = va_arg(ap, void*);
43                if (s == 0) {
44                        asm("call %0/n" : :"m"(this->fun));
45                        asm("add %0, %%esp/n": :"m"(inc));
46                        break;
47                }else {
48                        asm("push %0/n" : :"m"(arg[argc]));
49                }
50                argc++;
51        }
52        delete[] arg;
53        va_end(ap);
54}
55 
56Method::Method(void* fun)
57{
58        this->fun = fun;
59}

核心的内容总是占用了很大的代码实现,理论上来说,使用汇编会更少一些,大部分的代码都是在对参数进行处理,由于C/C++没有对不定参数参数长度的处理(我没有找到,如果有谁记得找到通知我一声)。这里使用了判断参数是否为0来决定参数是否结束。
首先第一步统计参数的个数,每个参数限定为指针。
第二步复制参数,按照倒着的顺序复制参数,这样使得接下来的调用函数的参数顺序正确。
第三步就是汇编的调用:

va_start(ap, obj);
while (true) {
void* s = va_arg(ap, void*);
if (s == 0) {
asm("call %0/n" : :"m"(this->fun));
asm("add %0, %%esp/n": :"m"(inc));
break;
}else {
asm("push %0/n" : :"m"(arg[argc]));
}
argc++;
}

将参数按照模拟数序push到堆栈中,然后,再模拟函数调用,最后平衡堆栈。
下面是一个使用的例子:

ClassLoader* loader = new ClassLoader();

// create a new instance of this class using new classloader
Object* boot = Class::forName("liblauncher.so", false, loader).newInstance();

Method* m1 = boot->getClass().getMethod("launch", (Class*) null);
m1->invoke(boot, NULL);
delete m1;
delete loader;

注意:
上述的方法其实还是有一定的限制的,要求参数必须是指针或者整型,对于传递引用可能会引起问题,另外一个就是我因为使用的0来判断结束,所以如果参数传递了0就可能会出问题,更好地办法是传递一个表示参数个数的值。

原创粉丝点击