kvm类加载-006

本文介绍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 */

}

其步骤如下:

  1. 获得方法的数量

  2. 计算并分配大小.计算的公式为:

    (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. 加载每一个方法

  4. 检查是否有重复的方法

其中,第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 方法 就介绍完毕了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值