本文介绍kvm加载方法的部分.在jvm规范中,),包括实例初始化方法和类初始化方法(§2.9)在内,都由method_info结构所定义。在一个Class文件中,不会有两个方法同时具有相同的方法名和描述符(§ 4.3.3)。method_info结构格式如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
method_info结构各项的说明如下:
-
access_flags
在KVM中识别的值为:
- ACC_PUBLIC
- ACC_PRIVATE
- ACC_PROTECTED
- ACC_STATIC
- ACC_FINAL
- ACC_SYNCHRONIZED
- ACC_NATIVE
- ACC_ABSTRACT
- ACC_STRICT
一个方法只能设置ACC_PRIVATE,ACC_PROTECTED和ACC_PUBLI三个标志中的一个标志;如果一个方法被设置ACC_ABSTRACT标志,则这个方法不能被设置ACC_FINAL,ACC_NATIVE, ACC_PRIVATE, ACC_STATIC, ACC_STRICT和ACC_SYNCHRONIZED标志。
所有的接口方法必须被设置ACC_ABSTRACT和ACC_PUBLIC标志;但是不能再设置其它的标志了(JLS §9.4)。
实例初始化方法只能设置ACC_PRIVATE,ACC_PROTECTED和ACC_PUBLIC中的一个标志;但是不能再设置其它的标志了(JLS §9.4)。
类初始化方法(§2.9)由Java虚拟机隐式自动调用,它的access_flags项的值除了ACC_STRICT标志,其它的标志都将被忽略。
-
name_index
name_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,它要么表示初始化方法的名字(<init>或<clinit>),要么表示一个方法的有效的非全限定名(§4.2.2)
-
descriptor_index
descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,表示一个有效的方法的描述符(§4.3.3)
-
attributes_count
attributes_count的项的值表示这个方法的附加属性(§4.7)的数量。
-
attributes[]
attributes表的每一个成员的值必须是attribute(§4.7)结构,一个方法可以有任意个与之相关的属性。
在kvm中识别的属性为Code, Exceptions.
其中code的格式如下:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
}
exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count]; }
Exceptions属性是一个变长属性,它位于method_info(§4.6)结构的属性表中。Exceptions属性指出了一个方法需要检查的可能抛出的异常。一个method_info结构中最多
只能有一个Exceptions属性。其格式如下:
Exceptions_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions];
}
加载方法
这部分的代码是在j2me_cldc/kvm/VmCommon/src/loader.c 中实现的.代码如下:
static void
loadMethods(FILEPOINTER_HANDLE ClassFileH, INSTANCE_CLASS CurrentClass,
POINTERLIST_HANDLE StringPoolH)
{
// 1. 获得方法的数量
unsigned short methodCount = loadShort(ClassFileH);
if (methodCount == 0) {
return;
}
// 2. 计算并分配大小
START_TEMPORARY_ROOTS
int tableSize = SIZEOF_METHODTABLE(methodCount);
unsigned int index;
#if USESTATIC
DECLARE_TEMPORARY_ROOT(METHODTABLE, methodTable,
(METHODTABLE)callocObject(tableSize, GCT_METHODTABLE));
#else
METHODTABLE methodTable = (METHODTABLE)callocPermanentObject(tableSize);
#endif
methodTable->length = methodCount;
CurrentClass->methodTable = methodTable;
#if INCLUDEDEBUGCODE
if (traceclassloadingverbose) {
fprintf(stdout, "Loading methods\n");
}
#endif /* INCLUDEDEBUGCODE */
// 3. 加载每一个方法
for (index = 0; index < methodCount; index++) {
#if USESTATIC
START_TEMPORARY_ROOTS
DECLARE_TEMPORARY_METHOD_ROOT(thisMethod, methodTable, index);
loadOneMethod(ClassFileH, CurrentClass, &thisMethod, StringPoolH);
END_TEMPORARY_ROOTS
#else
METHOD thisMethod = &methodTable->methods[index];
loadOneMethod(ClassFileH, CurrentClass, &thisMethod, StringPoolH);
#endif
}
END_TEMPORARY_ROOTS
// 4. 检查是否有重复的方法
if (methodCount >= 2) {
/* Check to see if there are two methods with the same name/type */
METHODTABLE methodTable = CurrentClass->methodTable;
METHOD firstMethod = &methodTable->methods[0];
METHOD lastMethod = firstMethod + (methodCount - 1);
METHOD outer, inner;
for (outer = firstMethod; outer < lastMethod; outer++) {
for (inner = outer + 1; inner <= lastMethod; inner++) {
if (outer->nameTypeKey.i == inner->nameTypeKey.i) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_DUPLICATE_METHOD_FOUND);
}
}
}
}
#if INCLUDEDEBUGCODE
if (traceclassloadingverbose) {
fprintf(stdout, "Methods loaded ok\n");
}
#endif /* INCLUDEDEBUGCODE */
}
其步骤如下:
-
获得方法的数量
-
计算并分配大小.计算的公式为:
(sizeof(struct methodTableStruct) + 3) >> 2 + (n - 1) * (sizeof(struct methodStruct) + 3) >> 2
methodTableStruct的格式如下:
struct methodTableStruct { long length; // 该方法表的数量 struct methodStruct methods[1]; };
methodStruct的定义如下:
struct methodStruct { NameTypeKey nameTypeKey; union { struct { BYTE* code; // 字节码指针 HANDLERTABLE handlers; union { STACKMAP pointerMap; POINTERLIST verifierMap; } stackMaps; // stackMaps unsigned short codeLength; // 字节码的长度 unsigned short maxStack; } java; // 对应java方法 struct { void (*code)(void); void *info; } native; // 对应本地方法 } u; long accessFlags; /* 该方法的访问标记 */ INSTANCE_CLASS ofClass; /* 该方法所对应的类 */ unsigned short frameSize; /* 帧的大小 arguments+local vars */ unsigned short argCount; /* 参数的数量 */ };
-
加载每一个方法
-
检查是否有重复的方法
其中,第3步所对应的方法为: loadOneMethod。其代码如下:
static void
loadOneMethod(FILEPOINTER_HANDLE ClassFileH, INSTANCE_CLASS CurrentClass,
METHOD_HANDLE thisMethodH, POINTERLIST_HANDLE StringPoolH)
{
METHOD thisMethod;
// 1. 加载访问标记, nameIndex, typeIndex
unsigned short accessFlags =
loadShort(ClassFileH) & RECOGNIZED_METHOD_FLAGS;
unsigned short nameIndex = loadShort(ClassFileH);
unsigned short typeIndex = loadShort(ClassFileH);
START_TEMPORARY_ROOTS
// 2. 获得方法名,方法签名
DECLARE_TEMPORARY_ROOT(const char *, methodName,
getUTF8String(StringPoolH, nameIndex));
DECLARE_TEMPORARY_ROOT(const char *, signature,
getUTF8String(StringPoolH, typeIndex));
NameTypeKey result;
// 3. 校验访问标记
if (strcmp(methodName, "<clinit>") == 0) {
accessFlags = ACC_STATIC;
} else {
verifyMethodFlags(accessFlags,
CurrentClass->clazz.accessFlags,
methodName);
}
verifyName(methodName, LegalMethod);
result.nt.nameKey =
change_Name_to_Key(&methodName, 0, strlen(methodName));
result.nt.typeKey =
change_MethodSignature_to_Key(&signature, 0, strlen(signature));
ASSERTING_NO_ALLOCATION
// 4. 计算参数的数量
thisMethod = unhand(thisMethodH);
thisMethod->nameTypeKey = result;
thisMethod->argCount = verifyMethodType(methodName, signature);
/* 如果该方法不是静态的,则还需添加一个,对应this*/
if (!(accessFlags & ACC_STATIC)) {
thisMethod->argCount++;
}
// 如果参数数量大于255,则抛出异常
if (thisMethod->argCount > 255) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_TOO_MANY_METHOD_ARGUMENTS);
}
#if INCLUDEDEBUGCODE
if (traceclassloadingverbose) {
fprintf(stdout, "Method '%s' loaded\n", methodName);
}
#endif /* INCLUDEDEBUGCODE */
/* Check if the field is double length, or is a pointer type. If so
* set the appropriate bit in the word */
switch (strchr(signature, ')')[1]) {
case 'D': case 'J': accessFlags |= ACC_DOUBLE; break;
case 'L': case '[': accessFlags |= ACC_POINTER; break;
case 'V': accessFlags |= (ACC_POINTER | ACC_DOUBLE); break;
}
thisMethod->accessFlags = accessFlags;
thisMethod->ofClass = CurrentClass;
/* These values will be initialized later */
thisMethod->frameSize = 0;
thisMethod->u.java.maxStack = 0;
thisMethod->u.java.handlers = NIL;
END_ASSERTING_NO_ALLOCATION
// 5. 加载方法的属性
loadMethodAttributes(ClassFileH, thisMethodH, StringPoolH);
/* Garbage collection may have happened */
thisMethod = unhand(thisMethodH);
if (!(thisMethod->accessFlags & (ACC_NATIVE | ACC_ABSTRACT))) {
if ( thisMethod->frameSize < thisMethod->argCount) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_BAD_FRAME_SIZE);
}
}
// 6. 处理本地方法
if (accessFlags & ACC_NATIVE) {
/* Store native function pointer in the code field */
thisMethod->u.native.info = NULL;
thisMethod->u.native.code =
getNativeFunction(CurrentClass, methodName, signature);
/* Check for finalizers, skipping java.lang.Object */
if (CurrentClass->superClass != NULL) {
if (strcmp(methodName, "finalize") == 0) {
if (accessFlags & ACC_PRIVATE) {
/* private native finalize() method found */
/* Save native finalizer pointer in the class field */
CurrentClass->finalizer =
(NativeFuncPtr)thisMethod->u.native.code;
}
}
}
}
END_TEMPORARY_ROOTS
}
加载方法属性
对应的方法为:loadMethodAttributes.代码如下:
static void
loadMethodAttributes(FILEPOINTER_HANDLE ClassFileH,
METHOD_HANDLE thisMethodH,
POINTERLIST_HANDLE StringPoolH)
{
// 1. 获得方法属性的数量
unsigned short attrCount = loadShort(ClassFileH);
METHOD thisMethod = unhand(thisMethodH);
int attrIndex;
bool_t needCode = !(thisMethod->accessFlags & (ACC_NATIVE | ACC_ABSTRACT));
bool_t needExceptionTable = TRUE; /* always optional */
/* 2. 遍历属性 */
for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
unsigned short attrNameIndex = loadShort(ClassFileH);
unsigned int attrLength = loadCell(ClassFileH);
char* attrName = getUTF8String(StringPoolH, attrNameIndex);
/* 2.1 读取Code属性 */
if (strcmp(attrName, "Code") == 0) {
unsigned int actualLength;
if (!needCode) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_DUPLICATE_CODE_ATTRIBUTE);
}
// 加载code 属性
actualLength = loadCodeAttribute(ClassFileH, thisMethodH, StringPoolH);
if (actualLength != attrLength) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_BAD_CODE_ATTRIBUTE_LENGTH);
}
needCode = FALSE;
} else if (!strcmp(attrName, "Exceptions")) {// 2.2 读取Exceptions 属性
/* Do minimal checking of this attribute. */
unsigned int exceptionCount;
unsigned int i;
if (!needExceptionTable) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_DUPLICATE_EXCEPTION_TABLE);
}
needExceptionTable = FALSE;
exceptionCount = loadShort(ClassFileH);
if (2 * exceptionCount + 2 != attrLength) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_BAD_EXCEPTION_ATTRIBUTE);
}
for (i = 0; i < exceptionCount; i++) {
unsigned short exception = loadShort(ClassFileH);
if (exception == 0) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_BAD_EXCEPTION_ATTRIBUTE);
} else {
verifyConstantPoolEntry(unhand(thisMethodH)->ofClass,
exception, CONSTANT_Class);
}
}
} else {
/* 2.3 跳过其他属性 */
skipBytes(ClassFileH, attrLength);
}
}
// 3. 如果该方法不是本地方法,抽象方法,如果没有定义Code属性,则抛出异常
if (needCode) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_MISSING_CODE_ATTRIBUTE);
}
}
这里比较关键的是loadCodeAttribute,代码如下:
static unsigned int
loadCodeAttribute(FILEPOINTER_HANDLE ClassFileH,
METHOD_HANDLE thisMethodH,
POINTERLIST_HANDLE StringPoolH)
{
unsigned int actualAttrLength;
unsigned int codeLength;
int nCodeAttrs;
int codeAttrIndex;
BYTE* code;
bool_t needStackMap = TRUE;
METHOD thisMethod = unhand(thisMethodH);
thisMethod->u.java.maxStack = loadShort(ClassFileH); /* 读取 maxstack */
thisMethod->frameSize = loadShort(ClassFileH); /* 读者栈帧大小 */
codeLength = loadCell(ClassFileH); /* 读取code的length */
/* 在kvm中,code不能大于32KB */
if (codeLength >= 0x7FFF) {
raiseExceptionWithMessage(OutOfMemoryError,
KVM_MSG_METHOD_LONGER_THAN_32KB);
}
/* 在kvm中,不能有超过512个局部变量*/
if (thisMethod->u.java.maxStack + thisMethod->frameSize >
MAXIMUM_STACK_AND_LOCALS) {
raiseExceptionWithMessage(OutOfMemoryError,
KVM_MSG_TOO_MANY_LOCALS_AND_STACK);
}
/* 在持久代中分配大小 */
if (USESTATIC && !ENABLEFASTBYTECODES) {
code = (BYTE *)mallocBytes(codeLength);
} else {
code = (BYTE *)callocPermanentObject(ByteSizeToCellSize(codeLength));
}
thisMethod = unhand(thisMethodH);
thisMethod->u.java.code = code;
thisMethod->u.java.codeLength = codeLength;
// 复制字节码
loadBytes(ClassFileH, (char *)thisMethod->u.java.code, codeLength);
actualAttrLength = 2 + 2 + 4 + codeLength;
/* Load exception handlers associated with the method */
actualAttrLength += loadExceptionHandlers(ClassFileH, thisMethodH);
nCodeAttrs = loadShort(ClassFileH);
actualAttrLength += 2;
// 读取属性
for (codeAttrIndex = 0; codeAttrIndex < nCodeAttrs; codeAttrIndex++) {
unsigned short codeAttrNameIndex = loadShort(ClassFileH);
unsigned int codeAttrLength = loadCell(ClassFileH);
char* codeAttrName = getUTF8String(StringPoolH, codeAttrNameIndex);
/* Check if the attribute contains stack maps */
// 读取StackMap
if (!strcmp(codeAttrName, "StackMap")) {
unsigned int stackMapAttrSize;
if (!needStackMap) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_DUPLICATE_STACKMAP_ATTRIBUTE);
}
needStackMap = FALSE;
stackMapAttrSize = loadStackMaps(ClassFileH, thisMethodH);
if (stackMapAttrSize != codeAttrLength) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_BAD_ATTRIBUTE_SIZE);
}
} else {
// 跳过其他属性
skipBytes(ClassFileH, codeAttrLength);
}
actualAttrLength += 6 + codeAttrLength;
}
return actualAttrLength;
}
对于复制字节码,其最终调用的方法为loadBytesInternal:
static int
loadBytesInternal(FILEPOINTER_HANDLE ClassFileH, char *buffer, int pos,
int length, bool_t checkEOF)
{
int n;
FILEPOINTER ClassFile = unhand(ClassFileH);
if (!ClassFile->isJarFile) {// 对应class文件
FILE *file = ((struct stdioPointerStruct*)ClassFile)->file;
/*
* If 'pos' is -1 then just read sequentially. Used internally by
* loadBytes() which is called from classloader.
*/
if (pos != -1)
fseek(file, pos, SEEK_SET);
n = fread(buffer, 1, length, file); // 使用fread函数读取
if (n != length) {
if (checkEOF) {
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_CLASSFILE_SIZE_DOES_NOT_MATCH);
} else {
if (n > 0)
return (n);
else
return (-1); /* eof */
}
} else {
return (n);
}
} else {// 对应jar包中的情况
struct jarPointerStruct *ds = (struct jarPointerStruct *)ClassFile;
int avail;
if (pos == -1)
pos = ds->dataIndex;
avail = ds->dataLen - pos;
if (avail < length && checkEOF) /* inconceivable? */{
raiseExceptionWithMessage(ClassFormatError,
KVM_MSG_CLASSFILE_SIZE_DOES_NOT_MATCH);
} else {
if (avail) {
int readLength = (avail > length) ? length : avail;
memcpy(buffer, &ds->data[pos], readLength); // 使用memcpy 读取
ds->dataIndex = pos + readLength;
return (readLength);
} else {
return (-1);
}
}
}
return (0);
}
加载本地方法
这部分涉及的数据结构如下:
typedef void NativeFunctionType(void); // 本地方法的指针
typedef NativeFunctionType *NativeFunctionPtr;
typedef struct {
const char *const name; // 方法名称
const char *const signature; // 签名
const NativeFunctionPtr implementation; // 本地方法指针
} NativeImplementationType;
typedef struct {
const char *const packageName; // 包名
const char *const baseName; // 类名
const NativeImplementationType *const implementation;
} ClassNativeImplementationType;
extern const ClassNativeImplementationType nativeImplementations[]; // 本地函数表
加载本地方法的实现为:
NativeFunctionPtr
getNativeFunction(INSTANCE_CLASS clazz, const char* methodName,
const char *methodSignature)
{
#if !ROMIZING
const ClassNativeImplementationType *cptr;
const NativeImplementationType *mptr;
UString UBaseName = clazz->clazz.baseName;
UString UPackageName = clazz->clazz.packageName;
char* baseName;
char* packageName;
// 获得包名
if (UPackageName == NULL) {
packageName = "";
} else {
packageName = UStringInfo(UPackageName);
}
// 获得类名
if (UBaseName == NULL) {
baseName = "";
} else {
baseName = UStringInfo(UBaseName);
}
// 遍历本地方法表
for (cptr = nativeImplementations; cptr->baseName != NULL ; cptr++) {
if ( (xstrcmp(cptr->packageName, packageName) == 0)
&& (xstrcmp(cptr->baseName, baseName) == 0)) {
break;
}
}
// 遍历实现,如果找到方法名,方法签名都相同的就返回,否则返回null
for (mptr = cptr->implementation; mptr != NULL ; mptr++) {
const char *name = mptr->name;
if (name == NULL) {
return NULL;
}
if (strcmp(name, methodName) == 0) {
const char *signature = mptr->signature;
/* The signature is NULL for non-overloaded native methods. */
if (signature == NULL || (xstrcmp(signature, methodSignature) == 0)){
return mptr->implementation;
}
}
}
#endif /* !ROMIZING */
return NULL;
}
至此, loadRawClass 方法 就介绍完毕了.