kvm类加载-003

这里再贴一下, loadRawClass 方法的步骤:

  1. 打开对应的class文件
  2. 加载版本号
  3. 加载常量池 <— 本文讲解该步骤.
  4. 加载类的标识符
  5. 加载接口
  6. 加载字段
  7. 加载方法
  8. 加载类的属性
  9. 关闭流

常量池的数据结构

常量池定义如下:

struct constantPoolStruct { 
    union constantPoolEntryStruct entries[1];
};

该数据结构稍微复杂一些。常量池中的第一个(下标为0)元素包含常量池中的条目数。
实际常量池条目位于条目[1]——条目[length-1]中。

最后一个常量池条目[长度-1]之后紧接着是一个字节数组,其中包含指示每个常量池条目类型的标记.如图所示:

在这里插入图片描述

加载常量池

加载常量池的方法为:loadConstantPool,位于 j2me_cldc/kvm/VmCommon/src/loader.c. 代码如下,有删减:

static POINTERLIST
loadConstantPool(FILEPOINTER_HANDLE ClassFileH, INSTANCE_CLASS CurrentClass)
{
    unsigned short constantCount = loadShort(ClassFileH);

    CONSTANTPOOL ConstantPool;
    int cpIndex;
    POINTERLIST result;
    int lastNonUtfIndex = -1;

    // 1. 声明要使用的RawPool, StringPool
    START_TEMPORARY_ROOTS
	    /*
	     * CONSTANTPOOL_ENTRY RawPool = (
        RawPool = (CONSTANTPOOL_ENTRY)mallocBytes(constantCount * (1 + sizeof(*RawPool))),                                                 \
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&RawPool,
        RawPool);
	     */
        DECLARE_TEMPORARY_ROOT(CONSTANTPOOL_ENTRY, RawPool,
            (CONSTANTPOOL_ENTRY)mallocBytes(constantCount * (1 + sizeof(*RawPool))));// 1 + sizeof(*RawPool) 是为了分配标记数组,看CONSTANTPOOL的注释
        
        /* 由于在后续流程中需要使用常量池中的信息,如类名,方法标识符,因此,将这些信息保存在StringPool 中
        
         * POINTERLIST StringPool = (
        StringPool = (POINTERLIST)callocObject((((sizeof(struct pointerListStruct) + 3) >> 2)+((constantCount)-1)), GCT_POINTERLIST),                                                 \
        TemporaryRoots[TemporaryRootsLength++].cellp = (cell *)&StringPool,   \
        StringPool);
         */
        DECLARE_TEMPORARY_ROOT(POINTERLIST, StringPool,
            (POINTERLIST)callocObject(SIZEOF_POINTERLIST(constantCount), GCT_POINTERLIST));
            
        StringPool->length = constantCount;
#define RAW_POOL(i)      (RawPool[i])
#define CP_ENTRY(i)      (ConstantPool->entries[i])

        /* 2. 读取常量池元素 */
        for (cpIndex = 1; cpIndex < constantCount; cpIndex++) {
            unsigned char tag = loadByte(ClassFileH);
            unsigned char *Tags = (unsigned char *)(&RawPool[constantCount]);
            // 2.1 将tag分配在常量池条目的最后
            Tags[cpIndex] = tag;

            switch (tag) {
            case CONSTANT_String:
            case CONSTANT_Class: {
                /* A single 16-bit entry that points to a UTF string */
                unsigned short nameIndex = loadShort(ClassFileH);
                RAW_POOL(cpIndex).integer = nameIndex;
                break;
            }

            .... 省略其他代码

            default:
                raiseExceptionWithMessage(ClassFormatError,
                    KVM_MSG_INVALID_CONSTANT_POOL_ENTRY);
                break;
            }

            if (tag != CONSTANT_Utf8) {
                lastNonUtfIndex = cpIndex;
            }
        }

        {
            /* 
             * 3. 分配内存,其大小为所有的entries + tag
             */
            int numberOfEntries = lastNonUtfIndex + 1;
            int tableSize =
                numberOfEntries + ((numberOfEntries + (CELL - 1)) >> log2CELL); // 对齐
#if USESTATIC
            IS_TEMPORARY_ROOT(ConstantPool,
                (CONSTANTPOOL)callocObject(tableSize, GCT_NOPOINTERS));
#else
            ConstantPool = (CONSTANTPOOL)callocPermanentObject(tableSize);
#endif
            CONSTANTPOOL_LENGTH(ConstantPool) = numberOfEntries;
            CurrentClass->constPool = ConstantPool;
        }

        /* 4.   创建常量池元素,复制数据 */
        for (cpIndex = 1; cpIndex < constantCount; cpIndex++) {
            /* These can move between iterations of the loop */
            unsigned char *Tags = (unsigned char *)(&RawPool[constantCount]);
            unsigned char *CPTags = CONSTANTPOOL_TAGS(ConstantPool);// ((unsigned char *)&(ConstantPool)->entries[CONSTANTPOOL_LENGTH(ConstantPool)])

            unsigned char tag = Tags[cpIndex];
            if (cpIndex <= lastNonUtfIndex) {
                CPTags[cpIndex] = tag;
            }

            switch (tag) {

            case CONSTANT_Class: {
                unsigned short nameIndex =
                    (unsigned short)RAW_POOL(cpIndex).integer;
                START_TEMPORARY_ROOTS
                    DECLARE_TEMPORARY_ROOT(const char *, name,
                        getUTF8String(&StringPool, nameIndex));
                    verifyName(name, LegalClass);
                    CP_ENTRY(cpIndex).clazz =
                        getRawClassX(&name, 0, strlen(name));
                END_TEMPORARY_ROOTS
                break;
            }

            case CONSTANT_String: {
                unsigned short nameIndex =
                    (unsigned short)RAW_POOL(cpIndex).integer;
                char *name = getUTF8String(&StringPool, nameIndex);
                INTERNED_STRING_INSTANCE string =
                    internString(name, strlen(name));
                CP_ENTRY(cpIndex).String = string;
                break;
            }
            
            .... 省略其他代码

            default:
                raiseExceptionWithMessage(ClassFormatError,
                    KVM_MSG_INVALID_CONSTANT_POOL_ENTRY);
                break;
            }
        }
        result = StringPool;
    END_TEMPORARY_ROOTS
    return result;
}

其步骤如下:

  1. 声明要使用的RawPool, StringPool。由于在后续流程中需要使用常量池中的信息,如类名,方法标识符,因此,将这些信息保存在StringPool 中。
  2. 读取常量池元素
  3. 在内存中为ConstantPool分配内存,其大小为所有的entries + tag
  4. 复制数据

其中,重点是在 第2,4步.我们分开来了.

读取常量元素

再读取元素时,首先,将常量池元素的tag保存在RawPool的后面.代码如下:

unsigned char tag = loadByte(ClassFileH);
unsigned char *Tags = (unsigned char *)(&RawPool[constantCount]);
Tags[cpIndex] = tag;

接下来,根据常量池的元素,就不同的判断. 在JVM规范中,对常量池项的tag的定义是:

在这里插入图片描述

CONSTANT_String,CONSTANT_Class

代码如下:

case CONSTANT_String:
case CONSTANT_Class: {
    /* A single 16-bit entry that points to a UTF string */
    unsigned short nameIndex = loadShort(ClassFileH);
    RAW_POOL(cpIndex).integer = nameIndex;
    break;
}

因此,在 RAW_POOL(cpIndex) 中保存的是CONSTANT_String, CONSTANT_Class所对应的下标,而在RAW_POOL的后面,在对应下标的位置分别保存的是8,7.

CONSTANT_Fieldref,CONSTANT_Methodref,CONSTANT_InterfaceMethodref

代码如下:

case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref: {
    /* Two 16-bit entries */
    unsigned short classIndex = loadShort(ClassFileH);
    unsigned short nameTypeIndex = loadShort(ClassFileH);
    RAW_POOL(cpIndex).method.classIndex = classIndex;
    RAW_POOL(cpIndex).method.nameTypeIndex = nameTypeIndex;
    break;
}

因此,在RAW_POOL(cpIndex) 中保存的是三者的class的下标,标识符的下标.而在RAW_POOL的后面,在对应下标的位置分别保存的是9,10,11.

CONSTANT_Float,CONSTANT_Integer

case CONSTANT_Float:
#if !IMPLEMENTS_FLOAT
    fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
#endif
case CONSTANT_Integer:
{
    /* A single 32-bit value */
    long value = loadCell(ClassFileH);
    RAW_POOL(cpIndex).integer = value;
    break;
}

因此,在RAW_POOL(cpIndex)中保存的是float,int的值.而在RAW_POOL的后面,在对应下标的位置分别保存的是4,3.

CONSTANT_Double,CONSTANT_Long

case CONSTANT_Double:
#if !IMPLEMENTS_FLOAT
    fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
#endif
case CONSTANT_Long:
    /* A 64-bit value */
    RAW_POOL(cpIndex).integer = loadCell(ClassFileH);
    cpIndex++;
    if (cpIndex >= constantCount) {
        raiseExceptionWithMessage(ClassFormatError,
            KVM_MSG_BAD_64BIT_CONSTANT);
    }
    /* set this location in the Tags array to 0 so it's
     * flagged as a bogus location if some TCK test decides
     * to read from the middle of this long constant pool entry.
     * 将标记数组中的这个位置设置为0,这样,如果某个TCK测试决定从这个长常量池条目的中间读取,它就会被标记为一个伪位置
     */
    Tags[cpIndex] = 0;
    RAW_POOL(cpIndex).integer = loadCell(ClassFileH);
    break;

double,long是64位,占两个字节,在常量池中占两个位置.而在RAW_POOL的后面,在对应下标的位置分别保存的是6,5.

CONSTANT_NameAndType

case CONSTANT_NameAndType: {
  // 常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构要么表示特殊的方法名<init>(§2.9),要么表示一个有效的字段或方法的非限定名(Unqualified Name)。
	unsigned short nameIndex = loadShort(ClassFileH);
	// 常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构表示一个有效的字段描述符(§4.3.2)或方法描述符(§4.3.3)。 
	unsigned short typeIndex = loadShort(ClassFileH);
	/* In the second pass, below, these will be replaced with the
	 * actual nameKey and typeKey.  Currently, they are indices
	 * to items in the constant pool that may not yet have been
	 * loaded */
	RAW_POOL(cpIndex).nameTypeKey.nt.nameKey = nameIndex;
	RAW_POOL(cpIndex).nameTypeKey.nt.typeKey = typeIndex;
	break;
}

因此,RAW_POOL中保存的是nameIndex, typeIndex的下标,而在RAW_POOL的后面,在对应下标的位置分别保存的是12

CONSTANT_Utf8

case CONSTANT_Utf8: {
	unsigned short length = loadShort(ClassFileH);
	/* This allocation may invalidate ClassFile */
	char *string = mallocBytes(length + 1);
	STRING_POOL(cpIndex) =  string;
	loadBytes(ClassFileH, string, length);
	string[length] = '\0';
	
	verifyUTF8String(string, length);
}

这里和之前的情况不一样,将Utf8所代表的内容,保存在STRING_POOL中.而在RAW_POOL的后面,在对应下标的位置分别保存的是8.

复制数据

由于之前的环节,只是将常量池元素原封不动的读取,因此,这步将常量池元素进行解析.比如,对于CONSTANT_Class,之前只是保存的是name_index的值,而常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,代表一个有效的类或接口二进制名称的内部形式(也就是字符串).
经过这步处理后,常量池中保存的就是一个有效的类或接口二进制名称的内部形式(也就是字符串).

CONSTANT_Class

case CONSTANT_Class: {
                unsigned short nameIndex =
                    (unsigned short)RAW_POOL(cpIndex).integer;
                START_TEMPORARY_ROOTS
                    DECLARE_TEMPORARY_ROOT(const char *, name,
                        getUTF8String(&StringPool, nameIndex));
                    verifyName(name, LegalClass);
                    CP_ENTRY(cpIndex).clazz =
                        getRawClassX(&name, 0, strlen(name));
                END_TEMPORARY_ROOTS
                break;
            }

关于这部分上文有讲述.

CONSTANT_String

case CONSTANT_String: {
                unsigned short nameIndex =
                    (unsigned short)RAW_POOL(cpIndex).integer;
                char *name = getUTF8String(&StringPool, nameIndex);
                INTERNED_STRING_INSTANCE string =
                    internString(name, strlen(name));
                CP_ENTRY(cpIndex).String = string;
                break;
 }

这里将ConstantPool中的值该为指向在kvm内部的InternStringTable中,保存该字符串的指针

CONSTANT_Fieldref,CONSTANT_Methodref,CONSTANT_InterfaceMethodref

case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref: {
    /* Two 16-bit entries */
    unsigned short classIndex = RAW_POOL(cpIndex).method.classIndex;
    unsigned short nameTypeIndex =RAW_POOL(cpIndex).method.nameTypeIndex;
    // 进行校验
    if (classIndex >= constantCount ||
        Tags[classIndex] != CONSTANT_Class ||
        nameTypeIndex >= constantCount ||
        Tags[nameTypeIndex] != CONSTANT_NameAndType) {
        raiseExceptionWithMessage(ClassFormatError,
            KVM_MSG_BAD_FIELD_OR_METHOD_REFERENCE);
    } else {
        unsigned short nameIndex =
            RAW_POOL(nameTypeIndex).nameTypeKey.nt.nameKey;
        unsigned short typeIndex =
            RAW_POOL(nameTypeIndex).nameTypeKey.nt.typeKey;
        char* name = getUTF8String(&StringPool, nameIndex);
        char* type = getUTF8String(&StringPool, typeIndex);
        if (   (tag == CONSTANT_Fieldref && type[0] == '(')
            || (tag != CONSTANT_Fieldref && type[0] != '(')
            || (tag != CONSTANT_Fieldref &&
                        !strcmp(name, "<clinit>"))) {
            raiseExceptionWithMessage(ClassFormatError,
                KVM_MSG_BAD_NAME_OR_TYPE_REFERENCE);
        }
        CP_ENTRY(cpIndex) = RAW_POOL(cpIndex);
    }
    break;
}

这里是直接复制RAW_POOL保存的值,也就是保存的是三者的class的下标,标识符的下标.

CONSTANT_Double,CONSTANT_Long,CONSTANT_Float,CONSTANT_Integer

case CONSTANT_Double:
#if !IMPLEMENTS_FLOAT
                fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
#endif
case CONSTANT_Long:
    CP_ENTRY(cpIndex).integer = RAW_POOL(cpIndex).integer;
    cpIndex++;
    CPTags[cpIndex] = 0;
    CP_ENTRY(cpIndex).integer = RAW_POOL(cpIndex).integer;
    break;

case CONSTANT_Float:
#if !IMPLEMENTS_FLOAT
    fatalError(KVM_MSG_FLOATING_POINT_NOT_SUPPORTED);
#endif
case CONSTANT_Integer:
    CP_ENTRY(cpIndex).integer = RAW_POOL(cpIndex).integer;
    break;

这里是直接复制RAW_POOL保存的值.

CONSTANT_NameAndType

case CONSTANT_NameAndType: {
    unsigned short nameIndex = RAW_POOL(cpIndex).nameTypeKey.nt.nameKey;
    unsigned short typeIndex = RAW_POOL(cpIndex).nameTypeKey.nt.typeKey;
    START_TEMPORARY_ROOTS
        DECLARE_TEMPORARY_ROOT(const char *, name,
            getUTF8String(&StringPool, nameIndex));
        DECLARE_TEMPORARY_ROOT(const char *, type,
            getUTF8String(&StringPool, typeIndex));
        NameKey nameKey;
        unsigned short typeKey;
        if (type[0] == '(') {
            verifyName(name, LegalMethod);
            verifyMethodType(name, type);
            typeKey = change_MethodSignature_to_Key(&type, 0,
                                                    strlen(type));
        } else {
            verifyName(name, LegalField);
            verifyFieldType(type);
            typeKey = change_FieldSignature_to_Key(&type, 0,
                                                   strlen(type));
        }
        nameKey = change_Name_to_Key(&name, 0, strlen(name));
        CP_ENTRY(cpIndex).nameTypeKey.nt.nameKey = nameKey;
        CP_ENTRY(cpIndex).nameTypeKey.nt.typeKey = typeKey;
     END_TEMPORARY_ROOTS
    break;
}

对于CONSTANT_NameAndType,其在class的结构如下:

CONSTANT_NameAndType_info {  
u1 tag;  
u2 name_index;  
u2 descriptor_index;
} 
  • name_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构要么表示特殊的方法名<init>(§2.9),要么表示一个有效的字段或方法的非限定名(Unqualified Name)。
  • descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构表示一个有效的字段描述符(§4.3.2)或方法描述符(§4.3.3)。

因此,这里将ConstantPool中的值修改为nameKey, typeKey. 这里涉及的方法为change_MethodSignature_to_Key, change_FieldSignature_to_Key.比较简单,读者可自行查看.

CONSTANT_Utf8

case CONSTANT_Utf8:
/* We don't need these after loading time.  So why bother */
if (cpIndex <= lastNonUtfIndex) {
    CP_ENTRY(cpIndex).integer = 0;
    CPTags[cpIndex] = 0;
}
break;

对于该类型的元素而言,由于将常量池元素解析完毕后,该元素也就没用了,但是为了占位,也就将其值修改为0.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值