之前的几篇文章介绍了将一个类从RAW到linked的过程.这里我们再次回顾一下kvm内部的class的状态:
#define CLASS_RAW 0 /* this value must be 0 */
#define CLASS_LOADING 1
#define CLASS_LOADED 2
#define CLASS_LINKED 3
#define CLASS_VERIFIED 4
#define CLASS_READY 5
#define CLASS_ERROR -1
此时,class的状态只是CLASS_LINKED,还不能使用.后续还需要进行VERIFIE.分配内存,实例化,才能到达ready的状态.
VERIFIE
VERIFIE 是在initializeClass 中进行的,代码如下:
void initializeClass(INSTANCE_CLASS thisClass)
{
if (thisClass->status == CLASS_ERROR) {
raiseException(NoClassDefFoundError);
} else if (thisClass->status < CLASS_READY) {
if (thisClass->status < CLASS_VERIFIED) {
verifyClass(thisClass); // 1. 验证class
}
// 2. 如果该类不存在父类或父类的状态为CLASS_READY,同时该类没有clinit方法,则直接修改该类的状态为CLASS_READY
if ((thisClass->superClass == NULL ||
thisClass->superClass->status == CLASS_READY) &&
getSpecialMethod(thisClass,clinitNameAndType) == NULL) {
setClassStatus(thisClass,CLASS_READY);
}
else {
// 3. 其他情况,则通过字节码的方式,执行clinit 方法
TRY {
pushFrame(RunCustomCodeMethod);
pushStackAsType(CustomCodeCallbackFunction, &runClinit);
pushStackAsType(INSTANCE_CLASS, thisClass);
pushStackAsType(long, 1);
} CATCH (e) {
/* Stack overflow */
setClassStatus(thisClass, CLASS_ERROR);
THROW(e);
} END_CATCH
}
}
}
这里的步骤很简单:
- 验证class . 这是在这节要介绍的
- 如果该类不存在父类或父类的状态为CLASS_READY,同时该类没有clinit方法,则直接修改该类的状态为CLASS_READY.
- 其他情况,则通过字节码的方式,执行clinit 方法
其中,第3步会在进后续文章中介绍.
这里主要调用的是verifyClass.其代码如下:
int verifyClass(INSTANCE_CLASS thisClass) {
static int Vfy_verifyMethod(METHOD vMethod);
int i;
int result = 0;
#if USESTATIC
CONSTANTPOOL cp = thisClass->constPool;
#endif
// 1. 如果该类有方法表的话,也即该类中定义了方法
if (thisClass->methodTable) {
// 此处为宏,直接返回false
if (!checkVerifiedClassList(thisClass)) {
/* 则验证所有的方法 */
for (i = 0; i < thisClass->methodTable->length; i++) {
METHOD thisMethod = &thisClass->methodTable->methods[i];
/* 1.1 跳过 runCustomCode */
if (thisMethod == RunCustomCodeMethod) {
continue;
}
/* 1.2 跳过本地,抽象方法 */
if (thisMethod->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) {
continue;
}
/*
* 1.3 进行验证
*/
result = Vfy_verifyMethod(thisMethod);
if (result != 0) {
break;
}
}
if (result == 0) {
/* 1.4 如果验证失败的话,此处为宏 */
appendVerifiedClassList(thisClass);
}
}
/* 2. 验证StackMap */
for (i = 0; i < thisClass->methodTable->length; i++) {
METHOD thisMethod = &thisClass->methodTable->methods[i];
if (thisMethod->u.java.stackMaps.verifierMap != NULL) {
STACKMAP newStackMap = (result == 0)
// 进行真正的验证
? rewriteVerifierStackMapsAsPointerMaps(thisMethod)
: NULL;
#if !USESTATIC
thisMethod->u.java.stackMaps.pointerMap = newStackMap;
#else
int offset = (char *)&thisMethod->u.java.stackMaps.pointerMap
- (char *)cp;
modifyStaticMemory(cp, offset, &newStackMap, sizeof(STACKMAP));
#endif
}
}
}
// 3. 如果验证没有问题的话,则将状态修改为CLASS_VERIFIED
if (result == 0) {
thisClass->status = CLASS_VERIFIED;
} else {
// 3.2 如果有问题的话,则抛出异常
START_TEMPORARY_ROOTS
DECLARE_TEMPORARY_ROOT(char*, className, getClassName((CLASS)thisClass));
thisClass->status = CLASS_ERROR;
if (className) {
raiseExceptionWithMessage(VerifyError, className);
} else {
raiseException(VerifyError);
}
END_TEMPORARY_ROOTS
}
return result;
}
此处重要的方法为: Vfy_verifyMethod, rewriteVerifierStackMapsAsPointerMaps.以下分别进行讲解.
Vfy_verifyMethod
此处的代码为:
static int Vfy_verifyMethod(METHOD vMethod) {
void Vfy_verifyMethodOrAbort(const METHOD vMethod);
static bool_t Vfy_checkNewInstructions(METHOD vMethod);
int res;
#if INCLUDEDEBUGCODE
Vfy_printVerifyStartMessage(vMethod);
#endif
START_TEMPORARY_ROOTS
// 1. 获得maxStack,frameSize,并进行内存分配
vMaxStack = vMethod->u.java.maxStack;
vFrameSize = vMethod->frameSize;
// VERIFIERTYPE = unsigned short
IS_TEMPORARY_ROOT(vStack, (VERIFIERTYPE*) callocObject(ByteSizeToCellSize(vMaxStack * sizeof(VERIFIERTYPE)), GCT_NOPOINTERS));
IS_TEMPORARY_ROOT(vLocals, (VERIFIERTYPE*) callocObject(ByteSizeToCellSize(vFrameSize * sizeof(VERIFIERTYPE)), GCT_NOPOINTERS));
/*
* This bitmap keeps track of all the NEW instructions that we have
* already seen.
*/
IS_TEMPORARY_ROOT(NEWInstructions, NULL); // NEWInstructions = null
vSP = 0; /* 初始化 sp */
/*
* All errors from Vfy_verifyMethodOrAbort are longjmped here. If no error
* is thrown then res will be zero from the call to setjmp
* 设置当Vfy_verifyMethodOrAbort出错时,跳回的地方
*/
res = setjmp(vJumpBuffer);
if (res == 0) {
/*
* Setup the verification context and call the core function.
*/
methodBeingVerified = vMethod;
bytecodesBeingVerified = Mth_getBytecodes(vMethod);
// 进行真正的验证
Vfy_verifyMethodOrAbort(vMethod);
if (!Vfy_checkNewInstructions(vMethod)) {
// 如果出现异常,则会跳回到setjmp处,且返回值为VE_BAD_NEW_OFFSET
Vfy_throw(VE_BAD_NEW_OFFSET);
}
}
END_TEMPORARY_ROOTS
#if INCLUDEDEBUGCODE
Vfy_printVerifyEndMessage(vMethod, res);
#endif
return res;
}
这里的关键点是用到了 setjmp和longjmp. 当Vfy_verifyMethodOrAbort 抛出异常时,最终会经Vfy_throw处理,其代码如下:
void Vfy_throw(int code) {
longjmp(vJumpBuffer, code);
}
其会再次返回setjmp 处,但是此时的返回值非0,因此会直接返回res.
关于这点可以参考如下链接:
Vfy_verifyMethodOrAbort
该方法的核心思想是通过模拟执行引擎,执行字节码,验证操作栈,本地变量表上的类型是否和stackmap中的一致.其代码如下:
void Vfy_verifyMethodOrAbort(const METHOD vMethod) {
/*
* The following variables are constant in this function
* 1. 获取变量,并初始化一些变量
*/
const CLASS vClass = Mth_getClass(vMethod);
const CLASS vSuperClass = Cls_getSuper(vClass);
const int codeLength = Mth_getBytecodeLength(vMethod);
const int handlerCount = Mth_getExceptionTableLength(vMethod);
const CONSTANTPOOL vPool = Cls_getPool(vClass);
/*
* The virtual IP used by the verifier 设置虚拟ip值
*/
IPINDEX ip = 0;
/*
* The following is set to TRUE where is no direct control
* flow from current instruction to the next instruction
* in sequence.
* 当没有从当前指令到下一条指令的直接控制流时设置为真。
*/
bool_t noControlFlow = FALSE;
/*
* Pointer to the "current" stackmap entry 指向stackmap entry 的下标
*/
int currentStackMapIndex = 0;
/*
* Check that a virtual method does not subclass a "final"
* method higher up the inheritance chain
*
* Was bug 4336036
*/
// 2. 如果该方法不是静态的,则检查是否存在重写final方法的情况
if (!Mth_isStatic(vMethod)) {
/*
* Dont check methods in java.lang.Object. 如果该方法所对应的类为java.lang.Object,则不进行检查
*/
if (!Cls_isJavaLangObject(vClass)) {
/*
* Lookup the method being verified in the superclass
* 将当前类的继承结构中查找是否存在同名方法
*/
METHOD superMethod = Cls_lookupMethod(vClass, vSuperClass, vMethod);
/*
* If it exists then it must not be final
* 如果存在的话,同时该方法为final的,则抛出异常
* 为啥,此时的情况为父类,当前类有同名方法,而父类中方法为final,则此时不符合规范的
*/
if (superMethod != NULL && Mth_isFinal(superMethod)) {
Vfy_throw(VE_FINAL_METHOD_OVERRIDE);
}
}
}
/*
* Verify that all exception handlers have a reasonable exception type.
* 3. 验证exception handler
*/
if (handlerCount > 0) {
int i;
VERIFIERTYPE exceptionVerifierType;
/*
* Iterate through the handler table
*/
for (i = 0; i < handlerCount; i++) {
/*
* Get the catch type from the exception table
* 获得该exception handler 所catch 的异常类
*/
POOLINDEX catchTypeIndex = Mth_getExceptionTableCatchType(vMethod, i);
/*
* If the catch type index is zero then this is a try/finally entry
* and there is no exception type, If it is not zero then it needs
* to be checked.
* 如果为0,则意味着exception handler 是在try/finally 结构下产生的
*/
if (catchTypeIndex != 0) {
/*
* Check that the entry is there and that it is a CONSTANT_Class
* 检查catchTypeIndex 在常量池中的类型为CONSTANT_Class
*/
Pol_checkTagIsClass(vPool, catchTypeIndex);
/*
* Get the class key 获得该类所对应的key
*/
exceptionVerifierType = Vfy_toVerifierType(Pol_getClassKey(vPool, catchTypeIndex));
/*
* Check that it is subclass of java.lang.Throwable
* 如果该类不是java.lang.Throwable 的子类的话,则抛出异常
*/
if (!Vfy_isAssignable(exceptionVerifierType, Vfy_getThrowableVerifierType())) {
Vfy_throw(VE_EXPECT_THROWABLE);
}
}
}
}
/*
* Initialize the local variable type table with the method argument types
* 4. 初始化局部变量表
*/
Vfy_initializeLocals();
/*
* The main loop 主循环
* 5. 通过循环的方式,模拟字节码的执行
*/
while (ip < codeLength) {
IPINDEX original_ip = ip;// 保存当前的ip
/*
* Used to hold the current opcode 保存当前的操作码
*/
int opcode;
/*
* Used to hold IP address of the next entry in the stackmap table
*/
IPINDEX nextStackMapIP;
/*
* Setup the ip used for error messages in case it is needed later
*/
Vfy_setErrorIp(ip); // vErrorIp = ip;
/*
* Output the debug trace message 打印debug 信息
*/
Vfy_printVerifyLoopMessage(vMethod, ip);
/*
* Check that stackmaps are ordered according to offset and that
* every offset in stackmaps point to the beginning to an instruction.
* 检查stackmaps是否按照偏移量排序,stackmaps中的每个偏移量都指向指令的开头。
* StackMap jvm规范 P120
*/
nextStackMapIP = Mth_getStackMapEntryIP(vMethod, currentStackMapIndex); // 计算获得下一个StackMap 的 IP
if (nextStackMapIP == ip) {
currentStackMapIndex++; /* This offset is good. 修改currentStackMapIndex */
}
if (nextStackMapIP < ip) {
Vfy_throw(VE_BAD_STACKMAP); /* ip should have met offset. */
}
/*
* Trace the instruction ip 打印ip
*/
Vfy_trace1("Check instruction %ld\n", ip);
/*
* Merge with the next instruction (note how noControlFlow is used here).
*/
Vfy_checkCurrentTarget(ip, noControlFlow);
/*
* Set noControlFlow to its default state 设置默认值
*/
noControlFlow = FALSE;
/*
* Look for a possible jump target in one or more exception handlers.
* If found the current local variable types must be the same as those
* of the handler entry point.
* 5.1 如果当前ip是在异常表 中处理范围内,则需要校验错误类型是否和stack map 中的一致
*
*/
if (handlerCount > 0) {
int i;
/*
* Iterate through the handler table
*/
for (i = 0 ; i < handlerCount ; i++) {
/*
* Get the start and end points of the handler entry
*/
IPINDEX startPC = Mth_getExceptionTableStartPC(vMethod, i);
IPINDEX endPC = Mth_getExceptionTableEndPC(vMethod, i);
/*
* Check to see if the "current" ip falls in between these pointers
* 如果ip是在捕获范围内的话
*/
if (ip >= startPC && ip < endPC) {
VERIFIERTYPE exceptionVerifierType;
/*
* Get the ip offset for the exception handler and the catch type index
*/
IPINDEX handlerPC = Mth_getExceptionTableHandlerPC(vMethod, i); // 获得hander所对应的ip
POOLINDEX catchTypeIndex = Mth_getExceptionTableCatchType(vMethod, i); // 获得catch 类型
/*
* If the catch type index is zero then this is a try/finally entry.
* Unlike J2SE (1.3) there are no jsr/ret instructions in J2SE. The
* code in a finally block will be copied in to the end of the try
* block and an anonymous handler entry is make that points to another
* copy of the code. This will only be called if an exception is thrown
* in which case there will be an exception object of some kind on the
* stack. On the other hand if catch type index is non-zero then it
* will indicate a specific throwable type.
* 获得异常类型
*/
if (catchTypeIndex != 0) {
exceptionVerifierType = Vfy_toVerifierType(Pol_getClassKey(vPool, catchTypeIndex));
} else {
// 如果为0的话,则设置为throwable
exceptionVerifierType = Vfy_getThrowableVerifierType();
}
/*
* Save the current stack types somewhere and re-initialize the stack.
* 保存stack的状态
*/
Vfy_saveStackState();
/*
* Push the exception type and check the target has just this one type
* on the stack along with the current local variable types.
* 将exception 保存在栈中
*/
Vfy_push(exceptionVerifierType);
Vfy_checkHandlerTarget(handlerPC);
/*
* Restore the old stack types 恢复stack的状态
*/
Vfy_restoreStackState();
}
}
}
/*
* Get the next bytecode 获得字节码
*/
opcode = Vfy_getOpcode(ip);
// 模拟执行字节码
switch (opcode) {
case NOP: {
ip++;
break;
}
// 删去其他代码
default: {
Vfy_throw(VE_BAD_INSTR);
break;
}
} /* End of switch statement */
/* Make sure that there is no exception handler whose start or
* end is between original_ip and ip, because that would be
* an illegal value
* 验证在original_ip 和 ip 之前没有hander覆盖
*/
if (handlerCount > 0 && ip > original_ip + 1) {
int i;
for (i = 0 ; i < handlerCount ; i++) {
IPINDEX startPC = Mth_getExceptionTableStartPC(vMethod, i);
IPINDEX endPC = Mth_getExceptionTableEndPC(vMethod, i);
if ( (startPC > original_ip && startPC < ip)
|| (endPC > original_ip && endPC < ip)) {
Vfy_throw(VE_BAD_EXCEPTION_HANDLER_RANGE);
}
}
}
}
/* Make sure that the bytecode offset within
* stackmaps does not exceed code size
* 检查StackMapIndex 没有超过 StackMap
*/
if (Mth_checkStackMapOffset(vMethod, currentStackMapIndex) == 0) {
Vfy_throw(VE_BAD_STACKMAP);
}
// 如果ip 超过了 code 的长度,则抛出异常
if (ip > codeLength) {
Vfy_throw(VE_MIDDLE_OF_BYTE_CODE);
}
// 验证 在方法结束的时候不存在控制流,如果存在,则抛出异常
if (!noControlFlow) {
Vfy_throw(VE_FALL_THROUGH);
}
/* All done */
}
其步骤如下:
-
获取变量,并初始化一些变量
-
如果该方法不是静态的,则检查是否存在重写final方法的情况
-
验证exception handler,如果其捕获的异常不是java.lang.Throwable 的子类,则抛出异常
-
初始化局部变量表
-
通过循环的方式,模拟字节码的执行
- 如果当前ip是在异常表 中处理范围内,则需要校验错误类型是否和stack map 中的一致
- 模拟执行字节码
- 验证hander 不是在original_ip 和 ip 之间 开始或结束的,如果是的话,则是一个错误值.
-
在模拟完字节码后,进行如下检查:
- 检查StackMapIndex 没有超过 StackMap,否则抛出异常
- 如果ip 超过了 code 的长度,则抛出异常
- 验证 在方法结束的时候不存在控制流,如果存在,则抛出异常
这里面有几个比较重要的方法,下文一一介绍.
Cls_lookupMethod
这个方法是在父类中查找是否存在同名方法。此处是一个宏,定义如下:
#define Cls_lookupMethod(vClassContext, vClassTarget, vMethod) \
lookupMethod(vClassTarget, vMethod->nameTypeKey, (INSTANCE_CLASS)vClassContext)
此处会最终调用lookupMethod方法,代码如下:
METHOD lookupMethod(CLASS thisClass, NameTypeKey key,
INSTANCE_CLASS currentClass)
{
INSTANCE_CLASS thisInstanceClass =
IS_ARRAY_CLASS(thisClass) ? JavaLangObject : (INSTANCE_CLASS)thisClass;
// 1. 沿着继承体系不断向上查找,是否存在同名方法
do {
METHODTABLE methodTable = thisInstanceClass->methodTable;
FOR_EACH_METHOD(thisMethod, methodTable)
if (thisMethod->nameTypeKey.i == key.i) {
if ( currentClass == NULL // currentClass 为null
|| currentClass == thisInstanceClass
|| ((ACC_PUBLIC|ACC_PROTECTED) & thisMethod->accessFlags) // 该方法的修饰符为ACC_PUBLIC,ACC_PROTECTED
|| ( ((thisMethod->accessFlags & ACC_PRIVATE) == 0) // 该方法的修饰符不是private,并且currentClass,thisInstanceClass 在同一个包中
&& thisInstanceClass->clazz.packageName ==
currentClass->clazz.packageName)
) {
return thisMethod;
}
}
END_FOR_EACH_METHOD
/* If the class has a superclass, look its methods as well */
thisInstanceClass = thisInstanceClass->superClass;
} while (thisInstanceClass != NULL); // 不断的向上查找
/* Before we give up on an interface class, check any interfaces that
* it implements 2. 如果thisClass为接口的话,则检查该接口所继承的接口,是否存在同名方法*/
if ((thisClass->accessFlags & ACC_INTERFACE)
&& (((INSTANCE_CLASS)thisClass)->ifaceTable != NULL)) {
CONSTANTPOOL constantPool = ((INSTANCE_CLASS)thisClass)->constPool;
unsigned short *ifaceTable = ((INSTANCE_CLASS)thisClass)->ifaceTable;
int tableLength = ifaceTable[0];
int i;
for (i = 1; i <= tableLength; i++) {
int cpIndex = ifaceTable[i];
CLASS intf = constantPool->entries[cpIndex].clazz;
// 这里递归调用该方法
METHOD temp = lookupMethod(intf, key, currentClass);
if (temp != NULL) {
return temp;
}
}
}
return NULL;
}
这部分的代码比较简单,看注释就可以了.不再展开.
初始化局部变量表
该功能是通过Vfy_initializeLocals实现的,其代码如下:
void Vfy_initializeLocals() {
/* Fill in the initial derived stack map with argument types. 获得此时正在验证方法的签名 */
unsigned char *sig = (unsigned char*) change_Key_to_Name(methodBeingVerified->nameTypeKey.nt.typeKey, NULL);
int nargs = *sig++;
int i;
int n;
n = 0;
vNeedInitialization = FALSE;
// 1.如果该方法不是静态的话,则需要添加this指针
if (!Mth_isStatic(methodBeingVerified)) {
/* add one extra argument for instance methods */
n++; // 添加this
// 如果该方法不是JavaLangObject的<init>方法的话,则在vLocals[0] 中存放ITEM_InitObject,否则,存放当前方法所对应类
// 也就是local[0] 为this指针
if (methodBeingVerified->nameTypeKey.nt.nameKey == initNameAndType.nt.nameKey &&
methodBeingVerified->ofClass->clazz.key != Vfy_getObjectVerifierType()) {
vLocals[0] = ITEM_InitObject;
vNeedInitialization = TRUE;
} else {
vLocals[0] = methodBeingVerified->ofClass->clazz.key;
}
}
// 2.复制局部变量表
for (i = 0; i < nargs; i++) {
n += change_Arg_to_StackType(&sig, &vLocals[n]);
}
returnSig = sig;
}
同样,这部分的代码比较简单,不再展开.
Vfy_checkCurrentTarget
检查在指定ip的位置是否可以合并stack map.此处定义的是一个宏.定义如下:
#define Vfy_checkCurrentTarget(current_ip, noControlFlow) { \
Vfy_matchStackMap( \
current_ip, /* target ip */ \
SM_MERGE | (noControlFlow ? SM_EXIST : SM_CHECK), /* flags */ \
VE_SEQ_BAD_TYPE /* Vfy_throw code if not matched */ \
); \
}
#define Vfy_matchStackMap(targetIP, flags, throwCode) { \
if (!matchStackMap(methodBeingVerified, targetIP, flags)) { \
Vfy_throw(throwCode); \
} \
}
因此,最终会调用matchStackMap方法.在kvm内部定义了如下3个宏,用于控制如何比较两个堆栈映射的标志:
#define SM_CHECK 1 /* 检查派生类型是否与记录的类型匹配 */
#define SM_MERGE 2 /* 将记录的类型合并到派生类型中 */
#define SM_EXIST 4 /* 记录的类型属性必须存在*/
-
当noControlFlow为true时,传递的参数为 SM_MERGE | SM_EXIST
-
当noControlFlow为false时,传递的参数为 SM_MERGE | SM_CHECK
matchStackMap 方法如下:
bool_t matchStackMap(METHOD thisMethod, IPINDEX target_ip, int flags) {
bool_t result = TRUE; /* Assume result is TRUE */
bool_t target_needs_initialization;
unsigned short nstack;
unsigned short nlocals;
unsigned short i;
/* Following is volatile, and will disappear at first GC 1. 获得当前ip所对应的stackMap*/
unsigned short *stackMapBase = getStackMap(thisMethod, target_ip);
#if INCLUDEDEBUGCODE
if (traceverifier) {
fprintf(stdout, "Match stack maps (this=%ld, target=%ld)%s%s%s\n",
(long)vErrorIp, (long)target_ip,
(flags & SM_CHECK) ? " CHECK" : "",
(flags & SM_MERGE) ? " MERGE" : "",
(flags & SM_EXIST) ? " EXIST" : "");
}
#endif
// 如果不存在的话,则直接返回!(flags & SM_EXIST)
if (stackMapBase == NULL) {
#if INCLUDEDEBUGCODE
if (traceverifier) {
fprintf(stdout, "No recorded stack map at %ld\n", (long)target_ip);
}
#endif
return !(flags & SM_EXIST);
}
START_TEMPORARY_ROOTS
/*
* unsigned short* stackMap = ( \
\
stackMap = stackMapBase, \
TemporaryRoots[TemporaryRootsLength].cell = -1, \
TemporaryRoots[TemporaryRootsLength+1].cellpp = (cell **)&stackMap, \
TemporaryRoots[TemporaryRootsLength+2].cellp = (cell *)stackMapBase, \
TemporaryRootsLength = TemporaryRootsLength + 3, \
stackMap);
*/
DECLARE_TEMPORARY_ROOT_FROM_BASE(unsigned short*, stackMap,
stackMapBase, stackMapBase);
#if INCLUDEDEBUGCODE
if (traceverifier) {
static void printStackMap(METHOD thisMethod, IPINDEX ip);
printStackMap(thisMethod, target_ip);
}
#endif
nlocals = *stackMap++; // 获得local的数量
/* We implicitly need to still perform initialization if at least
* one local or stack variable contains ITEM_InitObject */
target_needs_initialization = FALSE;
for (i = 0; i < nlocals; i++) {
unsigned short ty = *stackMap++;
unsigned short mergedType = ty;
if (ty == ITEM_InitObject) {
target_needs_initialization = TRUE;
}
// 如果vLocals[i] 不是mergedType 的子类,其flags为必须存在,则直接返回false
if ((SM_CHECK & flags) && !vIsAssignable(vLocals[i], ty, &mergedType)) {
result = FALSE;
goto done;
}
// 如果flags 为SM_MERGE,则直接使用mergedType就可以了
if (SM_MERGE & flags) {
vLocals[i] = mergedType;
}
}
// 如果flags为SM_MERGE的话,则在nlocals-vFrameSize填充0. 栈帧的结构是有方法参数,局部变量表,操作数栈组成的
if (SM_MERGE & flags) {
for (i = nlocals; i < vFrameSize; i++) {
vLocals[i] = ITEM_Bogus;
}
}
// 验证栈
nstack = *stackMap++;
if ((SM_CHECK & flags) && nstack != vSP) {
result = FALSE;
goto done;
}
if (SM_MERGE & flags) {
vSP = nstack;
}
for (i = 0; i < nstack; i++) {
unsigned short ty = *stackMap++;
unsigned short mergedType = ty;
if (ty == ITEM_InitObject) {
target_needs_initialization = TRUE;
}
if ((SM_CHECK & flags) && !vIsAssignable(vStack[i], ty, &mergedType)) {
result = FALSE;
goto done;
}
if (SM_MERGE & flags) {
vStack[i] = mergedType;
}
}
// 如果现在处理的方法为<init>方法
if (methodBeingVerified->nameTypeKey.nt.nameKey
== initNameAndType.nt.nameKey) {
if (SM_CHECK & flags) {
if (vNeedInitialization && !target_needs_initialization) {
/* We still need to perform initialization, but we are
* merging into a location that doesn't.
*/
result = FALSE;
goto done;
}
}
if (SM_MERGE & flags) {
vNeedInitialization = target_needs_initialization;
}
}
done:
END_TEMPORARY_ROOTS
return result;
}
这里有两部分,1部分是验证局部变量表,1部分是验证操作数栈.其验证就是根据stackMap检查局部变量表,操作数栈的类型是否和记录中的一致,如不一致,就抛出异常.
Vfy_saveStackState,Vfy_restoreStackState
Vfy_saveStackState是保存操作数栈的状态,是需要和Vfy_restoreStackState一起连用的.其代码如下:
void Vfy_saveStackState() {
vSP_bak = vSP;
vStack0_bak = vStack[0];
vSP = 0;
}
void Vfy_restoreStackState() {
vStack[0] = vStack0_bak;
vSP = vSP_bak;
}
Vfy_checkHandlerTarget
该方法是为了hander 处,其栈帧是否正确.代码如下:
#define Vfy_checkHandlerTarget(target_ip) { \
Vfy_trace1("Check exception handler at %ld:\n", (long)target_ip); \
Vfy_matchStackMap(target_ip, SM_CHECK | SM_EXIST, VE_TARGET_BAD_TYPE); \
}
其最终也是调用了上文所介绍的Vfy_matchStackMap.
Vfy_push
验证最重要的方式是通过模拟操作数栈,模拟字节码的执行.这时就需要一个方法,执行压栈过程. Vfy_push就是做如此操作的。
void Vfy_push(VERIFIERTYPE typeKey) {
if (vSP >= vMaxStack) {
Vfy_throw(VE_STACK_OVERFLOW);
}
vStack[vSP++] = typeKey;
}
关于字节码模拟,在后续文章中讲述.