【简易JVM开发日志1】classLoader

【总目录/源代码】:https://blog.csdn.net/qq_40636117/article/details/94383044

【下一篇】:https://blog.csdn.net/qq_40636117/article/details/94415960

 

我们知道,哪怕运行像输出“Hello World”这样简单的程序,java虚拟机都要先加载相关的类。加载类文件需要通过类路径(classpath)来寻找。当然根据搜索类路径先后顺序又分成三种。我们不管,先简单粗暴一点,完成基本功能,后面再慢慢优化。

所以classLoader文件基本功能如下:

1、寻找类文件:通过遍历classpath下的所有文件查找该类文件,并返回该类文件的绝对路径(找不到该类,返回classpath)。

2、加载类文件字节码:通过该类文件的绝对路径,读取该类字节码信息。

3、存放信息:存放这些类的字节码,并提供读取该类字节码信息的方法。

 

一、寻找类文件

搜索文件的方法就不用多说了,就是遍历多叉树嘛,其中非文件夹的结点为叶子节点。但是也有不少问题需要解决的,比如:

(1)有的.class文件存放在.jar.zip里面,这怎么整?比如java标准库大部分在rt.jar里面。Go语言有archive/zip包,直接搜索。而C++好像没有提供这种包。本来想着直接解压.jar.zip的,但解压要耗费不少时间,效率感人。本人姿势水平不够,先留着,以后再说吧。

(2)重名怎么办?两个类文件都叫Object.class但路径不同的情况也是有的。这个问题好解决,可以根据类文件大小,创建日期,最后修改日期等选择一个出来加载嘛,小问题小问题。

 

实现这个功能的方法就命名为findFile好了。首先根路径(即classpath)和要加载的类文件名是必须的。由于想弄全局变量,我把findpath也传了进来。findpath=classpath,说明没找到该文件;反之则是找到了。

void findFile(string classpath,string fname,string &findpath){//文件句柄 

	long hFile = 0;
	 
	struct _finddata_t fInfo;//文件信息
	string p;


	if ((hFile = _findfirst(p.assign(classpath).append("\\*").c_str(), &fInfo)) != -1){
		do{
			
			if ((fInfo.attrib & _A_SUBDIR)){
				if (strcmp(fInfo.name, ".") != 0 && strcmp(fInfo.name, "..") != 0){
					findFile(p.assign(classpath).append("\\\\").append(fInfo.name),fname,findpath);
				}
			}else{
				if(fname==fInfo.name){
					findpath=classpath.append("\\\\").append(fInfo.name);	
				}
			}
		} while (_findnext(hFile, &fInfo) == 0);
		
		_findclose(hFile);
	}
}

 

二、读取类文件字节码信息

首先.class文件存放的是字节码,所以数据类型是byte就跑不掉了。C++没有这种类型,没关系,char不也是8位吗?那就拿char当byte用就行了。通过观察《自己动手写java虚拟机》作者贴出的字节码,我发现没有负数,那就是无符号(unsigned)了。

先开一个unsigned char的数组用于存放信息。本来不知道数组开多大还挺头疼的,后来查了一下发现居然可以读取文件的大小,简直了!于是就顺利读取出来了。

一开始对比读取的数据,发现有的地方不一样,吓了一跳,后来运行了作者提供的go语言版虚拟机才明白过来,原来是版本问题,好险。

//读取文件内容
fileData *readFile(string absPath){//绝对路径

	const char *path=absPath.data();
	
	FILE *fp = fopen(path, "rb");
	if(fp==NULL){
		return NULL;
	}

	unsigned char *fdata;//字节数组
  
	fseek(fp, 0, SEEK_END);
	int size = ftell(fp);//文件大小
	fseek(fp, 0, SEEK_SET);
	if(size > 0){
		fdata = (unsigned char *)calloc(size+1,sizeof(unsigned char));
	}

	/*读文件内容存入内存*/
	fread(fdata, size, 1, fp);
	fclose(fp);
	fdata[size] = '\0';

	fileData *data=new fileData(fdata,size);

	return data;
}

 

三、存放信息

读取完文件数据后,我们还要定义一个类,专门存放这些信息。为了以后读取方便,还要提供方法读取里面的内容。

查阅资料我发现解析字节码的时候不一定8位8位地读取信息,有可能16位(2字节),32位(4字节)甚至64位(4字节)地读取。而且不一定读取一个8位数据,也有可能读取若干个8位数据。为了方便,当然要做出相应的方法啦。

u1(8位数据)——>C++的unsingned char类型

u2(16位数据)——>C++的unsigned int类型

u4(32位数据)——>C++的unsigned long类型

u8(64位数据)——>C++的unsigned _int64数据

类:

/*
	fileData类:存放从.class文件里读取的字节数据,
	并提供若干方法供外部读取数据(8位,16位,32位,64位)以便解码
*/
class fileData{

	private:

		unsigned char *data;//存放数据(8位)
		int start;//开始读取数据的索引,用于数据处理
		int size;//大小
		//[0,start)为已经读取过的数据
		//[start,size)为未读取数据

	public:

		fileData(unsigned char *d,int s){
			data=d;size=s;
			start=0;
		}

		unsigned char* getData(){
			return data;
		}

		int getSize(){
			return size;
		}

		//读取1个8位数据:(unsigned char)1字节
		unsigned char readU1(){
			if(size-start>0){
				return data[start++];
			}
			return 0;
		}

		//读取1个16位数据:(unsigned int)2字节
		unsigned int readU2(){
			unsigned int read=0;
			int usize;
			size-start>2 ? usize=2 : usize=size-start;
			for(int i=usize-1;i>=0;--i){
				read+=data[start++]<<(8*i);
			}
			return read;
		}

		//读取1个32位数据:(unsigned long)4字节
		unsigned long readU4(){
			unsigned int read=0;
			int usize;
			size-start>4 ? usize=4 : usize=size-start;
			for(int i=usize-1;i>=0;--i){
				read+=data[start++]<<(8*i);
			}
			return read;
		}
		

		//读取1个64位数据:(unsigned __int64)8字节
		unsigned _int64 readU8(){
			unsigned _int64 read=0;
			int usize;
			size-start>8 ? usize=8 : usize=size-start;
			for(int i=usize-1;i>=0;--i){
				read+=data[start++]<<(8*i);
			}
			return read;
		}
		
		//读取若干位8位数据
		unsigned char* readBytes(unsigned int n){
			unsigned char* s=(unsigned char*)calloc(n+1,sizeof(unsigned char));
			for(int i=0;i<n;++i){
				s[i]=readU1();
			}
			s[n]='\0';//一定要加上这玩意,代表结束!!不然会一直找下去,找到'\0'为止
			return s;
		}

		//读取若干16位数据
		unsigned int* readU2s(int n){
			unsigned int* s=(unsigned int*)calloc(n,sizeof(unsigned int));
			for(int i=0;i<n;++i){
				s[i]=readU2();
			}
			return s;
		}
			
};

 

四、测试:读取数据

先测试一下读取Object.class文件里面的内容

解析倒是解析出来了,但我里当场去世就差那么一点点。

 

于是我也学着书本一样,把这些数据做一些处理:

首先由于是8位2进制数,所以数值在00~FF之间,所以转成16进制后,就能确保数值在1~2位;再经过特殊的处理,就能保证数值都在2位,工工整整,方便输出!

处理后的数据:

 

嗯,干净多了,后面就是解码这些字节码的工作了

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值