public static MethodData read(ClassData classData, MethodInfo methodDesc) throws FormatException {
        String methodName = methodDesc.resolveName();
        String descriptor = methodDesc.resolveDescriptor();

        List<Type> params = extractParamTypes(descriptor);
        Type returnType = extractReturnType(descriptor);

        boolean isStatic = methodDesc.accessFlags.contains(AccessFlags.STATIC);
        boolean isAbstract = methodDesc.accessFlags.contains(AccessFlags.ABSTRACT);
        boolean isNative = methodDesc.accessFlags.contains(AccessFlags.NATIVE);

        List<Instruction> code = null;
        StackMapTable stackMapTable = null;
        int stackFrameSize = 0, opStackSize = 0;
        for (Attribute attr : methodDesc.attributes) {
            if (attr.getType() == Attribute.AttributeType.CODE) {
                if (isAbstract) {
                    throw new FormatException("Abstract method contains code attribute");
                }
                Code codeDesc = (Code)attr;
                code = Instruction.readCode(codeDesc.code, classData.constantPool);
                stackFrameSize = codeDesc.maxLocals + 1;
                opStackSize = codeDesc.maxStack + 1;
                for (Attribute codeAttr : codeDesc.attributes) {
                    if (codeAttr.getType() == Attribute.AttributeType.STACK_MAP_TABLE) {
                        stackMapTable = (StackMapTable) codeAttr;
                    }
                }
            }
        }
        if (code == null && !(isNative || isAbstract)) throw new FormatException("Method doesn't contain code");

        final MethodData methodData = new MethodData(classData, methodName, isStatic, isAbstract, isNative, params, returnType, code, stackFrameSize, opStackSize);

        if (stackMapTable != null) {
            methodData.loadedStaticInfo = MethodStaticInfo.load(methodData, stackMapTable);
        }

        return methodData;
    }