public static CompileAttribute ReadAttribute(EndianBinaryReader reader, List<CompileConstant> constants)
        {
            short nameIndex = reader.ReadInt16();
            int attributeLength = reader.ReadInt32();

            var nameConstant = constants[nameIndex] as CompileConstantUtf8;
            if (nameConstant == null) throw new InvalidOperationException();

            var name = new string(new JavaTextEncoding().GetChars(nameConstant.Value));

            switch (name)
            {
                case "ConstantValue":
                    return new CompileAttributeConstantValue().Read(reader, constants, attributeLength);
                case "Code":
                    return new CompileAttributeCode().Read(reader, constants, attributeLength);
                case "Exceptions":
                    return new CompileAttributeExceptions().Read(reader, constants, attributeLength);
                case "InnerClasses":
                    return new CompileAttributeInnerClasses().Read(reader, constants, attributeLength);
                case "Synthetic":
                    return new CompileAttributeSynthetic().Read(reader, constants, attributeLength);
                case "SourceFile":
                    return new CompileAttributeSourceFile().Read(reader, constants, attributeLength);
                case "LineNumberTable":
                    return new CompileAttributeLineNumberTable().Read(reader, constants, attributeLength);
                case "LocalVariableTable":
                    return new CompileAttributeLocalVariableTable().Read(reader, constants, attributeLength);
                case "Deprecated":
                    return new CompileAttributeDeprecated().Read(reader, constants, attributeLength);
                case "StackMapTable":
                    return new CompileAttributeStackMapTable().Read(reader, constants, attributeLength);
                case "EnclosingMethod":
                    return new CompileAttributeEnclosingMethod().Read(reader, constants, attributeLength);
                case "Signature":
                    return new CompileAttributeSignature().Read(reader, constants, attributeLength);
                case "SourceDebugExtension":
                    return new CompileAttributeSourceDebugExtension().Read(reader, constants, attributeLength);
                case "LocalVariableTypeTable":
                    return new CompileAttributeLocalVariableTypeTable().Read(reader, constants, attributeLength);
                case "RuntimeVisibleAnnotations":
                    return new CompileAttributeRuntimeVisibleAnnotations().Read(reader, constants, attributeLength);
                case "RuntimeInvisibleAnnotations":
                    return new CompileAttributeRuntimeInvisibleAnnotations().Read(reader, constants, attributeLength);
                case "RuntimeVisibleParameterAnnotations":
                    return new CompileAttributeRuntimeVisibleParameterAnnotations().Read(reader, constants, attributeLength);
                case "RuntimeInvisibleParameterAnnotations":
                    return new CompileAttributeRuntimeInvisibleParameterAnnotations().Read(reader, constants, attributeLength);
                default:
                    return new CompileAttributeGeneric(name).Read(reader, constants, attributeLength);
            }
        }
        private static ElementValue ReadElementValue(EndianBinaryReader reader, List<CompileConstant> constants)
        {
            var value = new ElementValue();

            value.Tag = reader.ReadByte();

            switch ((char)value.Tag)
            {
                case 'B':
                case 'C':
                case 'D':
                case 'F':
                case 'I':
                case 'J':
                case 'S':
                case 'Z':
                case 's':
                    value.ConstValueIndex = reader.ReadInt16();
                    break;
                case 'e':
                    value.TypeNameIndex = reader.ReadInt16();
                    value.ConstNameIndex = reader.ReadInt16();
                    break;
                case 'c':
                    value.ClassInfoIndex = reader.ReadInt16();
                    break;
                case '@':
                    value.AnnotationValue = ReadAnnotation(reader, constants);
                    break;
                case '[':
                    short valueCount = reader.ReadInt16();
                    for (int i = 0; i < valueCount; i++)
                    {
                        value.Values.Add(ReadElementValue(reader, constants));
                    }
                    break;
            }

            return value;
        }
        internal static Annotation ReadAnnotation(EndianBinaryReader reader, List<CompileConstant> constants)
        {
            var annotation = new Annotation();

            annotation.TypeIndex = reader.ReadInt16();

            short valueCount = reader.ReadInt16();
            for (int i = 0; i < valueCount; i++)
            {
                short index = reader.ReadInt16();
                ElementValue value = ReadElementValue(reader, constants);

                annotation.ElementValues.Add(new Tuple<short, ElementValue>(index, value));
            }

            return annotation;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            short annotationCount = reader.ReadInt16();
            for (int i = 0; i < annotationCount; i++)
            {
                Annotations.Add(ReadAnnotation(reader, constants));
            }

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            Variables = new List<VariableTypeTableEntry>();

            short variableCount = reader.ReadInt16();
            for (int i = 0; i < variableCount; i++)
            {
                var variable = new VariableTypeTableEntry();

                variable.StartPC = reader.ReadInt16();
                variable.Length = reader.ReadInt16();
                variable.Name = reader.ReadInt16();
                variable.Signature = reader.ReadInt16();
                variable.Index = reader.ReadInt16();

                Variables.Add(variable);
            }

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            SourceFile = reader.ReadInt16();

            return this;
        }
        public override CompileConstant Read(EndianBinaryReader reader)
        {
            short length = reader.ReadInt16();

            Value = reader.ReadBytes(length);

            return this;
        }
 public override CompileConstant Read(EndianBinaryReader reader)
 {
     throw new NotImplementedException();
 }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            ClassIndex = reader.ReadInt16();
            MethodIndex = reader.ReadInt16();

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            ExceptionTable = new List<short>();

            short exceptionCount = reader.ReadInt16();
            for (int i = 0; i < exceptionCount; i++)
            {
                ExceptionTable.Add(reader.ReadInt16());
            }

            return this;
        }
            public void Read(EndianBinaryReader reader)
            {
                Tag = (VerificationType)reader.ReadByte();

                if (Tag == VerificationType.Uninitialized ||
                    Tag == VerificationType.Object)
                {
                    Value = reader.ReadInt16();
                }
            }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            MaxStack = reader.ReadInt16();
            MaxLocals = reader.ReadInt16();

            int codeLength = reader.ReadInt32();
            Code = reader.ReadBytes(codeLength);

            ExceptionTable = new List<ExceptionTableEntry>();
            short exceptionCount = reader.ReadInt16();
            for (int i = 0; i < exceptionCount; i++)
            {
                var exception = new ExceptionTableEntry();

                exception.StartPC = reader.ReadInt16();
                exception.EndPC = reader.ReadInt16();
                exception.HandlerPC = reader.ReadInt16();
                exception.CatchType = reader.ReadInt16();

                ExceptionTable.Add(exception);
            }

            Attributes = new List<CompileAttribute>();
            short attributeCount = reader.ReadInt16();
            for (int i = 0; i < attributeCount; i++)
            {
                Attributes.Add(ReadAttribute(reader, constants));
            }

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            Entries = new List<StackMapFrame>();

            short entryCount = reader.ReadInt16();
            for (int i = 0; i < entryCount; i++)
            {
                var entry = new StackMapFrame();

                entry.Type = reader.ReadByte();
                if (entry.Type <= 63)
                {
                    // SAME
                }
                else if (entry.Type >= 64 && entry.Type <= 127)
                {
                    // SAME_LOCALS_1_STACK_ITEM
                    var item = new VerificationTypeInfo();
                    item.Read(reader);

                    entry.Stack.Add(item);
                }
                else if (entry.Type == 247)
                {
                    // SAME_LOCALS_1_STACK_ITEM_EXTENDED
                    entry.OffsetDelta = reader.ReadInt16();

                    var item = new VerificationTypeInfo();
                    item.Read(reader);

                    entry.Stack.Add(item);
                }
                else if (entry.Type >= 248 && entry.Type <= 250)
                {
                    // CHOP
                    entry.OffsetDelta = reader.ReadInt16();
                }
                else if (entry.Type == 251)
                {
                    // SAME_FRAME_EXTENDED
                    entry.OffsetDelta = reader.ReadInt16();
                }
                else if (entry.Type >= 252 && entry.Type <= 254)
                {
                    // APPEND
                    entry.OffsetDelta = reader.ReadInt16();

                    var type = (short)entry.Type;
                    for (int x = 251; x < type; x++)
                    {
                        var item = new VerificationTypeInfo();
                        item.Read(reader);

                        entry.Locals.Add(item);
                    }
                }
                else if (entry.Type == 255)
                {
                    // FULL_FRAME
                    entry.OffsetDelta = reader.ReadInt16();

                    short localCount = reader.ReadInt16();
                    for (int x = 0; x < localCount; x++)
                    {
                        var item = new VerificationTypeInfo();
                        item.Read(reader);

                        entry.Locals.Add(item);
                    }

                    short stackCount = reader.ReadInt16();
                    for (int x = 0; x < stackCount; x++)
                    {
                        var item = new VerificationTypeInfo();
                        item.Read(reader);

                        entry.Stack.Add(item);
                    }
                }

                Entries.Add(entry);
            }

            return this;
        }
 public abstract CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length);
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            Data =reader.ReadBytes(length);

            return this;
        }
        public override CompileConstant Read(EndianBinaryReader reader)
        {
            Value = reader.ReadInt64();

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            Classes = new List<InnerClass>();

            short classCount = reader.ReadInt16();
            for (int i = 0; i < classCount; i++)
            {
                var c = new InnerClass();

                c.InnerClassInfo = reader.ReadInt16();
                c.OuterClassInfo = reader.ReadInt16();
                c.InnerName = reader.ReadInt16();
                c.InnerModifier = (Modifier)reader.ReadInt16();

                Classes.Add(c);
            }

            return this;
        }
 public abstract CompileConstant Read(EndianBinaryReader reader);
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            LineNumbers = new List<LineNumberTableEntry>();

            short lineCount = reader.ReadInt16();
            for (int i = 0; i < lineCount; i++)
            {
                var line = new LineNumberTableEntry();

                line.StartPC = reader.ReadInt16();
                line.LineNumber = reader.ReadInt16();

                LineNumbers.Add(line);
            }

            return this;
        }
        public override CompileConstant Read(EndianBinaryReader reader)
        {
            NameIndex = reader.ReadInt16();

            return this;
        }
        public override CompileAttribute Read(EndianBinaryReader reader, List<CompileConstant> constants, int length)
        {
            var parameterCount = reader.ReadByte();

            ParameterAnnotations = new List<List<CompileAttributeRuntimeVisibleAnnotations.Annotation>>();
            for (var i = 0; i < parameterCount; i++)
            {
                var parameter = new List<CompileAttributeRuntimeVisibleAnnotations.Annotation>();

                var annotationCount = reader.ReadInt16();
                for (var x = 0; x < annotationCount; x++)
                {
                    var annotation = CompileAttributeRuntimeVisibleAnnotations.ReadAnnotation(reader, constants);

                    parameter.Add(annotation);
                }

                ParameterAnnotations.Add(parameter);
            }

            return this;
        }