/** * Read the fields table * * @param data * @param cp * @param slotSize * @param loader */ private static FieldData[] readFields( ByteBuffer data, VmCP cp, int slotSize, VmClassLoader loader) { final int fcount = data.getChar(); if (fcount > 0) { final FieldData[] ftable = new FieldData[fcount]; for (int i = 0; i < fcount; i++) { int modifiers = data.getChar(); final String name = cp.getUTF8(data.getChar()); final String signature = cp.getUTF8(data.getChar()); final boolean isstatic = ((modifiers & Modifier.ACC_STATIC) != 0); // Read field attributes final int acount = data.getChar(); VmAnnotation[] rVisAnn = null; byte[] rawAnnotations = null; Object constantValue = null; for (int a = 0; a < acount; a++) { final String attrName = cp.getUTF8(data.getChar()); final int length = data.getInt(); if (isstatic && VmArray.equals(ConstantValueAttrName, attrName)) { constantValue = cp.getAny(data.getChar()); } else if (VmArray.equals(RuntimeVisibleAnnotationsAttrName, attrName)) { rawAnnotations = new byte[length]; data.slice().get(rawAnnotations); rVisAnn = readRuntimeAnnotations(data, cp, true, loader); } else if (VmArray.equals(RuntimeInvisibleAnnotationsAttrName, attrName)) { readRuntimeAnnotations(data, cp, false, loader); } else { skip(data, length); } } ftable[i] = new FieldData(name, signature, modifiers, constantValue, rVisAnn, rawAnnotations); } return ftable; } else { return null; } }
/** * Read a single annotation structure. * * @param data * @param cp */ private static VmAnnotation readAnnotation(ByteBuffer data, VmCP cp, boolean visible) { final String typeDescr = cp.getUTF8(data.getChar()); final int numElemValuePairs = data.getChar(); final VmAnnotation.ElementValue[] values; if (numElemValuePairs == 0) { values = VmAnnotation.ElementValue.EMPTY_ARR; } else if (visible) { values = new VmAnnotation.ElementValue[numElemValuePairs]; for (int i = 0; i < numElemValuePairs; i++) { final String elemName = cp.getUTF8(data.getChar()); final Object value = readElementValue(data, cp); values[i] = new VmAnnotation.ElementValue(elemName, value); } } else { values = VmAnnotation.ElementValue.EMPTY_ARR; for (int i = 0; i < numElemValuePairs; i++) { data.getChar(); // Skip name ref skipElementValue(data, cp); } } return new VmAnnotation(typeDescr, values); }
/** * Read the interfaces table * * @param data * @param cls * @param cp * @return Some flags */ private static int readInterfaces(ByteBuffer data, VmType cls, VmCP cp) { int flags = 0; final int icount = data.getChar(); if (icount > 0) { final VmImplementedInterface[] itable = new VmImplementedInterface[icount]; for (int i = 0; i < icount; i++) { final VmConstClass icls = cp.getConstClass(data.getChar()); final String iclsName = icls.getClassName(); itable[i] = new VmImplementedInterface(iclsName); for (PragmaInterface pi : INTERFACE_PRAGMAS) { if (iclsName.equals(pi.className)) { flags |= pi.flags; } } } cls.setInterfaceTable(itable); } return flags; }
/** * Decode the data of a Exceptions attribute * * @param data * @param cls * @param cp * @return The read exceptions */ private static final VmExceptions readExceptions(ByteBuffer data, VmType cls, VmCP cp) { // Read the exceptions char pragmaFlags = 0; int pragmas = 0; final int ecount = data.getChar(); final VmConstClass[] list = new VmConstClass[ecount]; for (int i = 0; i < ecount; i++) { final int idx = data.getChar(); final VmConstClass ccls = cp.getConstClass(idx); list[i] = ccls; for (MethodPragmaException mp : METHOD_PRAGMA_EXCEPTIONS) { if (ccls.getClassName().equals(mp.className)) { pragmaFlags |= mp.flags; pragmas++; list[i] = null; break; } } } if (pragmas > 0) { final int newCnt = ecount - pragmas; if (newCnt == 0) { return new VmExceptions(null, pragmaFlags); } else { final VmConstClass[] newList = new VmConstClass[newCnt]; int k = 0; for (int i = 0; i < ecount; i++) { final VmConstClass ccls = list[i]; if (ccls != null) { newList[k++] = ccls; } } return new VmExceptions(newList, pragmaFlags); } } else { return new VmExceptions(list, pragmaFlags); } }
/** * Decode the data of a code-attribute * * @param data * @param cls * @param cp * @param method * @return The read code */ private static final VmByteCode readCode(ByteBuffer data, VmType cls, VmCP cp, VmMethod method) { final int maxStack = data.getChar(); final int noLocals = data.getChar(); final int codelength = data.getInt(); final ByteBuffer code = readBytes(data, codelength); // Read the exception Table final int ecount = data.getChar(); final VmInterpretedExceptionHandler[] etable = new VmInterpretedExceptionHandler[ecount]; for (int i = 0; i < ecount; i++) { final int startPC = data.getChar(); final int endPC = data.getChar(); final int handlerPC = data.getChar(); final int catchType = data.getChar(); etable[i] = new VmInterpretedExceptionHandler(cp, startPC, endPC, handlerPC, catchType); } // Read the attributes VmLineNumberMap lnTable = null; VmLocalVariableTable lvTable = VmLocalVariableTable.EMPTY; final int acount = data.getChar(); for (int i = 0; i < acount; i++) { final String attrName = cp.getUTF8(data.getChar()); final int len = data.getInt(); if (VmArray.equals(LineNrTableAttrName, attrName)) { lnTable = readLineNrTable(data); } else if (VmArray.equals(LocalVariableTableAttrName, attrName)) { lvTable = readLocalVariableTable(data, cp); } else { skip(data, len); } } return new VmByteCode(method, code, noLocals, maxStack, etable, lnTable, lvTable); }
/** * Read the method table * * @param data * @param rejectNatives * @param cls * @param cp * @param statics * @param cl */ private static void readMethods( ByteBuffer data, boolean rejectNatives, VmType cls, VmCP cp, VmStatics statics, VmClassLoader cl) { final int mcount = data.getChar(); if (mcount > 0) { final VmMethod[] mtable = new VmMethod[mcount]; for (int i = 0; i < mcount; i++) { final int modifiers = data.getChar(); final String name = cp.getUTF8(data.getChar()); final String signature = cp.getUTF8(data.getChar()); final boolean isStatic = ((modifiers & Modifier.ACC_STATIC) != 0); final VmMethod mts; final boolean isSpecial = name.equals("<init>"); // final int staticsIdx = statics.allocMethod(); if (isStatic || isSpecial) { if (isSpecial) { mts = new VmSpecialMethod(name, signature, modifiers, cls); } else { mts = new VmStaticMethod(name, signature, modifiers, cls); } } else { mts = new VmInstanceMethod(name, signature, modifiers, cls); } // statics.setMethod(staticsIdx, mts); mtable[i] = mts; // Read methods attributes final int acount = data.getChar(); VmAnnotation[] rVisAnn = null; VmAnnotation[] rInvisAnn = null; for (int a = 0; a < acount; a++) { String attrName = cp.getUTF8(data.getChar()); int length = data.getInt(); if (VmArray.equals(CodeAttrName, attrName)) { mts.setBytecode(readCode(data, cls, cp, mts)); } else if (VmArray.equals(ExceptionsAttrName, attrName)) { mts.setExceptions(readExceptions(data, cls, cp)); } else if (VmArray.equals(RuntimeVisibleAnnotationsAttrName, attrName)) { byte[] buf = new byte[length]; data.slice().get(buf); mts.setRawAnnotations(buf); // todo will get obsolate with openjdk based annotation support // rVisAnn = readRuntimeAnnotations(data, cp, true, cl); rVisAnn = readRuntimeAnnotations2(data, cp, true, cl, cls); } else if (VmArray.equals(RuntimeInvisibleAnnotationsAttrName, attrName)) { rInvisAnn = readRuntimeAnnotations(data, cp, false, cl); } else if (VmArray.equals(RuntimeVisibleParameterAnnotationsAttrName, attrName)) { byte[] buf = new byte[length]; data.slice().get(buf); mts.setRawParameterAnnotations(buf); // todo will get obsolate with openjdk based annotation support readRuntimeParameterAnnotations(data, cp, true, cl); } else if (VmArray.equals(RuntimeInvisibleParameterAnnotationsAttrName, attrName)) { readRuntimeParameterAnnotations(data, cp, false, cl); } else if (VmArray.equals(AnnotationDefaultAttrName, attrName)) { // todo will get obsolate with openjdk based annotation support byte[] buf = new byte[length]; data.slice().get(buf); mts.setRawAnnotationDefault(buf); Class r_class; VmType vtm = mts.getReturnType(); if (vtm.isPrimitive()) { r_class = getClassForJvmType(vtm.getJvmType()); } else { try { r_class = Class.forName(vtm.getName(), false, vtm.getLoader().asClassLoader()); } catch (ClassNotFoundException cnf) { throw new RuntimeException(cnf); } } Object defo = AnnotationParser.parseMemberValue( r_class, data, new VmConstantPool(cls), VmUtils.isRunningVm() ? cls.asClass() : cls.asClassDuringBootstrap()); mts.setAnnotationDefault(defo); } else { skip(data, length); } } mts.setRuntimeAnnotations(rVisAnn); if (rVisAnn != null) { mts.addPragmaFlags(getMethodPragmaFlags(rVisAnn, cls.getName())); } if (rInvisAnn != null) { mts.addPragmaFlags(getMethodPragmaFlags(rInvisAnn, cls.getName())); } if ((modifiers & Modifier.ACC_NATIVE) != 0) { final VmByteCode bc = getNativeCodeReplacement(mts, cl, rejectNatives); if (bc != null) { mts.setModifier(false, Modifier.ACC_NATIVE); mts.setBytecode(bc); } else { if (rejectNatives) { throw new ClassFormatError("Native method " + mts); } } } } cls.setMethodTable(mtable); } }
/** * Decode a given class. * * @param data * @param rejectNatives * @param clc * @param protectionDomain * @return The decoded class * @throws ClassFormatError */ private static final VmType decodeClass( ByteBuffer data, boolean rejectNatives, VmClassLoader clc, ProtectionDomain protectionDomain) throws ClassFormatError { final VmSharedStatics sharedStatics = clc.getSharedStatics(); final VmIsolatedStatics isolatedStatics = clc.getIsolatedStatics(); final int slotSize = clc.getArchitecture().getReferenceSize(); final int magic = data.getInt(); if (magic != 0xCAFEBABE) { throw new ClassFormatError("invalid magic"); } final int min_version = data.getChar(); final int maj_version = data.getChar(); if (false) { BootLogInstance.get().debug("Class file version " + maj_version + ';' + min_version); } final int cpcount = data.getChar(); // allocate enough space for the CP final byte[] tags = new byte[cpcount]; final VmCP cp = new VmCP(cpcount); for (int i = 1; i < cpcount; i++) { final int tag = data.get() & 0xFF; tags[i] = (byte) tag; switch (tag) { case 1: // Utf8 cp.setUTF8(i, readUTF(data)); break; case 3: // int cp.setInt(i, data.getInt()); break; case 4: // float // cp.setInt(i, data.getInt()); final int ival = data.getInt(); final float fval = Float.intBitsToFloat(ival); cp.setFloat(i, fval); break; case 5: // long cp.setLong(i, data.getLong()); i++; break; case 6: // double // cp.setLong(i, data.getLong()); final long lval = data.getLong(); final double dval = Double.longBitsToDouble(lval); cp.setDouble(i, dval); i++; break; case 7: // class cp.setInt(i, data.getChar()); break; case 8: // String cp.setInt(i, data.getChar()); break; case 9: // Fieldref case 10: // Methodref case 11: // IMethodref { final int clsIdx = data.getChar(); final int ntIdx = data.getChar(); cp.setInt(i, clsIdx << 16 | ntIdx); break; } case 12: // Name and Type { final int nIdx = data.getChar(); final int dIdx = data.getChar(); cp.setInt(i, nIdx << 16 | dIdx); break; } default: throw new ClassFormatError("Invalid constantpool tag: " + tags[i]); } } // Now patch the required entries (level 1) for (int i = 1; i < cpcount; i++) { switch (tags[i]) { case 7: { // Class final int idx = cp.getInt(i); final VmConstClass constClass = new VmConstClass(cp.getUTF8(idx)); cp.setConstClass(i, constClass); break; } case 8: { // String final int idx = cp.getInt(i); final int staticsIdx = sharedStatics.allocConstantStringField(cp.getUTF8(idx)); final VmConstString constStr = new VmConstString(staticsIdx); cp.setString(i, constStr); break; } } } // Now patch the required entries (level 2) for (int i = 1; i < cpcount; i++) { final int tag = tags[i]; if ((tag >= 9) && (tag <= 11)) { final int v = cp.getInt(i); final VmConstClass constClass = cp.getConstClass(v >>> 16); final int nat = cp.getInt(v & 0xFFFF); final String name = cp.getUTF8(nat >>> 16); final String descriptor = cp.getUTF8(nat & 0xFFFF); switch (tag) { case 9: // FieldRef cp.setConstFieldRef(i, new VmConstFieldRef(constClass, name, descriptor)); break; case 10: // MethodRef cp.setConstMethodRef(i, new VmConstMethodRef(constClass, name, descriptor)); break; case 11: // IMethodRef cp.setConstIMethodRef(i, new VmConstIMethodRef(constClass, name, descriptor)); break; } } } // Cleanup the unwantend entries for (int i = 1; i < cpcount; i++) { switch (tags[i]) { case 12: // Name and Type cp.reset(i); break; } } final int classModifiers = data.getChar(); final VmConstClass this_class = cp.getConstClass(data.getChar()); final String clsName = this_class.getClassName(); final VmConstClass super_class = cp.getConstClass(data.getChar()); final String superClassName; if (super_class != null) { superClassName = super_class.getClassName(); } else { superClassName = null; } // Allocate the class object final VmType cls; if (Modifier.isInterface(classModifiers)) { cls = new VmInterfaceClass(clsName, superClassName, clc, classModifiers, protectionDomain); } else { cls = new VmNormalClass(clsName, superClassName, clc, classModifiers, protectionDomain); } cls.setCp(cp); // Determine if we can safely align the fields // int pragmaFlags = 0; if (isBootType(cls)) { cls.addPragmaFlags(TypePragmaFlags.NO_FIELD_ALIGNMENT); } cls.addPragmaFlags(getClassNamePragmaFlags(clsName)); // Interface table cls.addPragmaFlags(readInterfaces(data, cls, cp)); // Field table final FieldData[] fieldData = readFields(data, cp, slotSize, clc); // Method Table readMethods(data, rejectNatives, cls, cp, sharedStatics, clc); // Read class attributes final int acount = data.getChar(); VmAnnotation[] rVisAnn = null; VmAnnotation[] rInvisAnn = null; String sourceFile = null; String signature = null; for (int a = 0; a < acount; a++) { final String attrName = cp.getUTF8(data.getChar()); final int length = data.getInt(); if (VmArray.equals(RuntimeVisibleAnnotationsAttrName, attrName)) { byte[] buf = new byte[length]; data.slice().get(buf); cls.setRawAnnotations(buf); rVisAnn = readRuntimeAnnotations(data, cp, true, clc); } else if (VmArray.equals(RuntimeInvisibleAnnotationsAttrName, attrName)) { rInvisAnn = readRuntimeAnnotations(data, cp, false, clc); } else if (VmArray.equals(SourceFileAttrName, attrName)) { sourceFile = cp.getUTF8(data.getChar()); } else if (VmArray.equals(SignatureAttrName, attrName)) { signature = cp.getUTF8(data.getChar()); } else { skip(data, length); } } cls.setRuntimeAnnotations(rVisAnn); cls.setSourceFile(sourceFile); cls.setSignature(signature); if (rInvisAnn != null) { cls.addPragmaFlags(getClassPragmaFlags(rInvisAnn, clsName)); } if (rVisAnn != null) { cls.addPragmaFlags(getClassPragmaFlags(rVisAnn, clsName)); } // Create the fields if (fieldData != null) { createFields(cls, fieldData, sharedStatics, isolatedStatics, slotSize, cls.getPragmaFlags()); } return cls; }
/** * Read a single element_value structure. * * @param data * @param cp */ private static Object readElementValue(ByteBuffer data, VmCP cp) { final int tag = data.get() & 0xFF; switch (tag) { case 'B': return (byte) cp.getInt(data.getChar()); case 'C': return (char) cp.getInt(data.getChar()); case 'D': return cp.getDouble(data.getChar()); case 'F': return cp.getFloat(data.getChar()); case 'I': return cp.getInt(data.getChar()); case 'J': return cp.getLong(data.getChar()); case 'S': return (short) cp.getInt(data.getChar()); case 'Z': return cp.getInt(data.getChar()) != 0; case 's': return cp.getAny(data.getChar()); case 'e': // enum { final String typeDescr = cp.getUTF8(data.getChar()); final String constName = cp.getUTF8(data.getChar()); return new VmAnnotation.EnumValue(typeDescr, constName); } case 'c': // class { final String classDescr = cp.getUTF8(data.getChar()); return new VmAnnotation.ClassInfo(classDescr); } case '@': // annotation return readAnnotation(data, cp, true); case '[': // array { final int numValues = data.getChar(); final Object[] arr = new Object[numValues]; for (int i = 0; i < numValues; i++) { arr[i] = readElementValue(data, cp); } return arr; } default: throw new ClassFormatError("Unknown element_value tag '" + (char) tag + '\''); } }
/** * Read a single annotation structure. * * @param data * @param cp */ private static VmAnnotation readAnnotation2( ByteBuffer data, VmCP cp, boolean visible, VmClassLoader loader, VmType vmType) { final String typeDescr = cp.getUTF8(data.getChar()); final int numElemValuePairs = data.getChar(); final VmAnnotation.ElementValue[] values; if (numElemValuePairs == 0) { values = VmAnnotation.ElementValue.EMPTY_ARR; } else if (visible) { values = new VmAnnotation.ElementValue[numElemValuePairs]; for (int i = 0; i < numElemValuePairs; i++) { final String elemName = cp.getUTF8(data.getChar()); Object defo = null; // readElementValue(data, cp); try { VmType annType = new Signature(typeDescr, loader).getType(); VmMethod mts = null; int dmc = annType.getNoDeclaredMethods(); for (int v = 0; v < dmc; v++) { VmMethod m = annType.getDeclaredMethod(v); if (elemName.equals(m.getName())) { mts = m; break; } } Class r_class; VmType vtm = mts.getReturnType(); if (vtm.isPrimitive()) { r_class = getClassForJvmType(vtm.getJvmType()); } else { try { r_class = vtm.getLoader().asClassLoader().loadClass(vtm.getName()); } catch (ClassNotFoundException cnf) { throw new RuntimeException(cnf); } } Class container; try { container = annType.getLoader().asClassLoader().loadClass(annType.getName()); } catch (ClassNotFoundException cnf) { throw new RuntimeException(cnf); } defo = AnnotationParser.parseMemberValue( r_class, data, new VmConstantPool(vmType), container); if (defo instanceof ExceptionProxy) throw new RuntimeException( "Error parsing annotation parameter value (annotation= " + annType.getName() + ", parameter=" + mts.getName() + ')'); } catch (Exception e) { throw new RuntimeException(e); } final Object value = defo; // readElementValue(data, cp); values[i] = new VmAnnotation.ElementValue(elemName, value); } } else { values = VmAnnotation.ElementValue.EMPTY_ARR; for (int i = 0; i < numElemValuePairs; i++) { data.getChar(); // Skip name ref skipElementValue(data, cp); } } return new VmAnnotation(typeDescr, values); }