1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use {Error, ErrorKind};
use raw;

use std::io::prelude::*;

use byteorder::{BigEndian, ReadBytesExt};

pub const TAG_UTF8: u8 = 1;
pub const TAG_INTEGER: u8 = 3;
pub const TAG_FLOAT: u8 = 4;
pub const TAG_LONG: u8 = 5;
pub const TAG_DOUBLE: u8 = 6;
pub const TAG_CLASS: u8 = 7;
pub const TAG_STRING: u8 = 8;
pub const TAG_FIELD_REF: u8 = 9;
pub const TAG_METHOD_REF: u8 = 10;
pub const TAG_INTERFACE_METHOD_REF: u8 = 11;
pub const TAG_NAME_AND_TYPE: u8 = 12;
pub const TAG_METHOD_HANDLE: u8 = 15;
pub const TAG_METHOD_TYPE: u8 = 16;
pub const TAG_INVOKE_DYNAMIC: u8 = 18;

/// An index into the constant table.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ConstantIndex(pub u16);

#[derive(Debug)]
pub enum Constant
{
    MethodRef {
        class: ConstantIndex,
        name_and_type: ConstantIndex,
    },
    InterfaceMethodRef {
        class: ConstantIndex,
        name_and_type: ConstantIndex,
    },
    FieldRef {
        class: ConstantIndex,
        name_and_type: ConstantIndex,
    },
    NameAndType {
        name: ConstantIndex,
        descriptor: ConstantIndex,
    },
    Class {
        name: ConstantIndex,
    },
    Utf8 {
        text: String,
    },
    String {
        /// An index to a 'UTF-8' constant.
        index: ConstantIndex,
    },
    Integer(i32),
    Long(i64),
    Float(f32),
    Double(f64),
}

impl Constant
{
    pub fn expect_utf8(&self) -> Result<String, Error> {
        if let Constant::Utf8 { ref text } = *self {
            Ok(text.clone())
        } else {
            Err(ErrorKind::MalformedFile(format!("expected utf-8 but got {:?}", self)).into())
        }
    }
}

impl raw::Serializable for Constant
{
    fn read(read: &mut Read) -> Result<Self, Error> {
        match read.read_u8()? {
            TAG_UTF8 => {
                let byte_count = read.read_u16::<BigEndian>()?;
                let bytes: Result<Vec<_>, _> = read.bytes().take(byte_count as _).collect();
                let s = String::from_utf8(bytes?).unwrap();

                Ok(Constant::Utf8 { text: s })
            }
            TAG_INTEGER => {
                let i = read.read_i32::<BigEndian>()?;
                Ok(Constant::Integer(i))
            },
            TAG_FLOAT => {
                let f = read.read_f32::<BigEndian>()?;
                Ok(Constant::Float(f))
            },
            TAG_LONG => {
                let i = read.read_i64::<BigEndian>()?;
                Ok(Constant::Long(i))
            },
            TAG_DOUBLE => {
                let f = read.read_f64::<BigEndian>()?;
                Ok(Constant::Double(f))
            },
            TAG_CLASS => {
                let name = ConstantIndex(read.read_u16::<BigEndian>()?);
                Ok(Constant::Class { name: name })
            },
            TAG_STRING => {
                let index = ConstantIndex(read.read_u16::<BigEndian>()?);
                Ok(Constant::String { index: index })
            },
            TAG_FIELD_REF => {
                let (class, name_and_type) = self::parse_reference(read)?;
                Ok(Constant::FieldRef { class: class,
                    name_and_type: name_and_type })
            },
            TAG_METHOD_REF => {
                let (class, name_and_type) = self::parse_reference(read)?;
                Ok(Constant::MethodRef { class: class,
                    name_and_type: name_and_type })
            },
            TAG_INTERFACE_METHOD_REF => {
                let (class, name_and_type) = self::parse_reference(read)?;
                Ok(Constant::InterfaceMethodRef { class: class,
                    name_and_type: name_and_type })
            },
            TAG_NAME_AND_TYPE => {
                let name = ConstantIndex(read.read_u16::<BigEndian>()?);
                let descriptor = ConstantIndex(read.read_u16::<BigEndian>()?);

                Ok(Constant::NameAndType { name: name, descriptor: descriptor })
            },
            TAG_METHOD_HANDLE => {
                unimplemented!();
            },
            TAG_METHOD_TYPE => {
                unimplemented!();
            },
            TAG_INVOKE_DYNAMIC => {
                unimplemented!();
            },
            i => Err(ErrorKind::MalformedFile(format!("invalid constant tag id: {}", i)).into()),
        }
    }

    fn write(&self, _write: &mut Write) -> Result<(), Error> {
        unimplemented!();
    }
}

/// Parses a reference.
/// These always have two u16s
fn parse_reference(read: &mut Read) -> Result<(ConstantIndex, ConstantIndex), Error> {
    let class_index = ConstantIndex(read.read_u16::<BigEndian>()?);
    let name_and_type_index = ConstantIndex(read.read_u16::<BigEndian>()?);
    Ok((class_index, name_and_type_index))
}