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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
 *     This file is part of Coffer.
 *
 *     Coffer is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Coffer is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with Coffer. (LICENSE.md)  If not, see <https://www.gnu.org/licenses/>.
 */
//! Loadable values. These types can be loaded on to the stack, and it is usually stored in the constant pool.

pub use crate::member::MemberRef;
pub use crate::ty::Type;

use crate::read_from;
use crate::prelude::*;
use std::hash::{Hash, Hasher};
use std::convert::TryFrom;

/// The kind of a method handle. It generally represents an instruction related to a member with one exception.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, ReadWrite)]
#[tag_type(u8)]
pub enum MethodHandleKind {
    /// A method handle reading from a virtual field.
    GetField = 1,
    /// A method handle reading from a static field.
    GetStatic,
    /// A method handle writing to a virtual field.
    PutField,
    /// A method handle writing to a static field.
    PutStatic,
    /// A method handle invoking a virtual method.
    InvokeVirtual,
    /// A method handle invoking a static method.
    InvokeStatic,
    /// A method handle invoking a special method.
    InvokeSpecial,
    /// A method handle invoking a construtor.
    NewInvokeSpecial,
    /// A method handle invoking an interface method.
    InvokeInterface,
}

impl TryFrom<u8> for MethodHandleKind {
    type Error = u8;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        use MethodHandleKind::*;
        Ok(match value {
            1 => GetField, 2 => GetStatic, 3 => PutField, 4 => PutStatic, 5 => InvokeVirtual,
            6 => InvokeStatic, 7 => InvokeSpecial, 8 => NewInvokeSpecial, 9 => InvokeInterface,
            v => return Err(v)
        })
    }
}

/// A method handle. It can be loaded on to the stack from the constant pool directly.
///
/// There are some restrictions of its kind and its member, one can use [`check`](MethodHandle::check) to check the validity.
#[derive(Clone, PartialEq, Hash, Debug)]
pub struct MethodHandle {
    /// The kind of the handle.
    pub kind: MethodHandleKind,
    /// The member that this handle points to.
    pub member: MemberRef
}

impl MethodHandle {
    /// Checks if this methodhandle is valid. Returns `Ok(())` if it is, returns `Err` with a detailed message if it is not.
    ///
    /// Rules:
    ///  - Handles with types [`GetField`](MethodHandleKind::GetField), [`GetStatic`](MethodHandleKind::GetStatic), [`PutField`](MethodHandleKind::PutField) and [`PutStatic`](MethodHandleKind::PutStatic) should have a `MemberRef` with a field type descriptor.
    ///  - Handles with types [`InvokeInterface`](MethodHandleKind::InvokeInterface), [`InvokeSpecial`](MethodHandleKind::InvokeSpecial), [`InvokeStatic`](MethodHandleKind::InvokeStatic), [`InvokeVirtual`](MethodHandleKind::InvokeVirtual) and [`NewInvokeSpecial`](MethodHandleKind::NewInvokeSpecial) should have a `MemberRef` with a method type descriptor.
    ///  - Handles cannot refer to static initializers, i.e. `MemberRef` with a name of `<clinit>`
    ///  - Handles with type [`NewInvokeSpecial`](MethodHandleKind::NewInvokeSpecial) must refer to a constructor method, i.e. `MemberRef` with a name of `<init>`
    ///  - Handles whose types are not [`NewInvokeSpecial`](MethodHandleKind::NewInvokeSpecial) cannot refer to a constructor method.
    ///
    pub fn check(&self) -> Result<()> {
        macro_rules! should_not_be_method {
            ($($kind: path),*) => {
                match self {
                    $(
                        MethodHandle {
                            kind: $kind,
                            member: MemberRef {
                                descriptor: Type::Method { .. },
                                ..
                            }
                        } => return Err(Error::Invalid("MethodHandle", concat!("kind is ", stringify!($kind), "but descriptor is method").into())),
                    )*
                    _ => {}
                }
            };
        }
        macro_rules! should_be_method {
            ($($kind:path),*) => {
                match self {
                    $(
                        MethodHandle {
                            kind: $kind,
                            member: MemberRef {
                                descriptor: Type::Method { .. },
                                ..
                            }
                        } => {},
                        MethodHandle {
                            kind: $kind,
                            ..
                        } => return Err(Error::Invalid("MethodHandle", concat!("kind is ", stringify!($kind), "but descriptor is NOT method").into())),
                    )*
                    _ => {}
                }
            };
        }
        should_not_be_method! { MethodHandleKind::GetField, MethodHandleKind::GetStatic, MethodHandleKind::PutField, MethodHandleKind::PutStatic }
        should_be_method! { MethodHandleKind::InvokeInterface, MethodHandleKind::InvokeSpecial, MethodHandleKind::InvokeStatic, MethodHandleKind::InvokeVirtual, MethodHandleKind::NewInvokeSpecial }
        match self.kind {
            MethodHandleKind::InvokeVirtual |
            MethodHandleKind::InvokeStatic |
            MethodHandleKind::InvokeSpecial |
            MethodHandleKind::InvokeInterface if self.member.name == "<init>" || self.member.name == "<clinit>" =>
                Err(Error::Invalid("MethodHandle", Cow::Borrowed("name must not be <init> or <clinit>"))),
            MethodHandleKind::NewInvokeSpecial if self.member.name != "<init>" =>
                Err(Error::Invalid("MethodHandle", Cow::Borrowed("name for NewInvokeSpecial must be <init>"))),
            _ => Ok(())
        }
    }
}
impl ConstantPoolReadWrite for MethodHandle {
    fn read_from<C: ConstantPoolReader, R: Read>(cp: &mut C, reader: &mut R) -> Result<Self, Error> {
        let kind = read_from!(reader)?;
        let member = read_from!(cp, reader)?;
        let res = Self {
            kind, member
        };
        res.check()?;
        Ok(res)
    }

    fn write_to<C: ConstantPoolWriter, W: Write>(&self, cp: &mut C, writer: &mut W) -> Result<(), Error> {
        self.check()?;
        self.kind.write_to(writer)?;
        self.member.write_to(cp, writer)
    }
}

/// A constant value that is located in the constant pool which can be loaded onto the stack.
#[derive(Clone, PartialEq, Debug)]
pub enum Constant {
    /// A 32 bit integer.
    I32(i32),
    /// A single-precision floating-point number.
    F32(f32),
    /// A 64 bit integer.
    I64(i64),
    /// A double-precision floating-point number.
    F64(f64),
    /// A String.
    String(Cow<'static, str>),
    /// A class. The packages are seperated by `/` instead of `.` i.e. `java/lang/String` instead of `java.lang.String`.
    Class(Cow<'static, str>),
    /// A member, can be a field or a method.
    Member(MemberRef),
    /// A method descriptor.
    MethodType(Type),
    /// A method handle.
    MethodHandle(MethodHandle)
}

impl Constant {
    /// returns `true` if this constant is a `f64` or `i64`, i.e., `long` or `double` in java.
    ///
    /// These types take up two slots in the stack.
    #[inline]
    pub const fn is_wide(&self) -> bool {
        matches!(self, Constant::I64(_) | Constant::F64(_))
    }

    /// Creates an instance of Constant that is a string.
    #[inline]
    pub fn string<T: Into<Cow<'static, str>>>(s: T) -> Self {
        Self::String(s.into())
    }
}

impl ConstantPoolReadWrite for Constant {
    fn read_from<C: ConstantPoolReader, R: Read>(cp: &mut C, reader: &mut R) -> Result<Self> {
        let idx = ReadWrite::read_from(reader)?;
        cp.read_constant(idx).ok_or_else(|| crate::error::Error::Invalid("constant pool entry index", idx.to_string().into()))
    }

    fn write_to<C: ConstantPoolWriter, W: Write>(&self, cp: &mut C, writer: &mut W) -> Result<()> {
        ReadWrite::write_to(&cp.insert_constant(self.clone()), writer)
    }
}

impl From<i32> for Constant {
    fn from(i: i32) -> Self {
        Self::I32(i)
    }
}

#[allow(clippy::derive_hash_xor_eq)]
// Hash cannot be directly derived for floating point types; hash by actual bits of the fp values because that is what will be written in byte form.
impl Hash for Constant {
    fn hash<H: Hasher>(&self, state: &mut H) {
        std::mem::discriminant(self).hash(state);
        match self {
            Constant::I32(i) => i.hash(state),
            Constant::F32(f) => (*f).to_bits().hash(state),
            Constant::I64(i) => i.hash(state),
            Constant::F64(f) => (*f).to_bits().hash(state),
            Constant::String(s) => s.hash(state),
            Constant::Class(s) => s.hash(state),
            Constant::MethodType(s) => s.hash(state),
            Constant::MethodHandle(m) => m.hash(state),
            Constant::Member(mem) => mem.hash(state)
        }
    }
}