#![warn(missing_docs)]
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::io::Read;
use std::mem::replace;
mod annotations;
mod convert;
mod error;
mod internal;
pub use annotations::AnnotationIter;
pub use convert::FromJava;
pub use error::{ConversionError, JavaError, StreamError};
use internal::{Flag, JavaStream, Marker};
#[cfg(feature = "derive")]
pub use jaded_derive::FromJava;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub type Result<T> = std::result::Result<T, JavaError>;
pub type ReadResult<T> = std::result::Result<T, StreamError>;
pub type ConversionResult<T> = std::result::Result<T, ConversionError>;
type Handle = u32;
const MAGIC: u16 = 0xAC_ED;
const VERSION: u16 = 0x00_05;
const NULL_HANDLE: Handle = 0;
const INITIAL_HANDLE: u32 = 0x7E_00_00;
pub struct Parser<T> {
stream: Box<T>,
references: HashMap<Handle, Reference>,
handle: Handle,
handle_stack: Vec<Handle>,
_version: u16,
}
impl<T: Read> Parser<T> {
pub fn new(mut source: T) -> Result<Self> {
let magic = source.read_u16()?;
let version = source.read_u16()?;
match (magic, version) {
(MAGIC, VERSION) => Ok(Self {
stream: Box::new(source),
_version: version,
handle_stack: vec![],
handle: INITIAL_HANDLE,
references: HashMap::new(),
}),
(MAGIC, v) => Err(JavaError::ReadError(StreamError::UnknownVersion(v))),
(m, _) => Err(JavaError::ReadError(StreamError::NonJavaObject(m))),
}
}
fn next_handle(&mut self) -> Handle {
let next = self.handle + 1;
self.handle_stack.push(self.handle);
replace(&mut self.handle, next)
}
fn read_from_stream(&mut self) -> ReadResult<StreamRecord> {
use StreamRecord::*;
Ok(match self.stream.read_marker()? {
Marker::Null => Ref(NULL_HANDLE),
Marker::Reference => Ref(self.read_reference()?),
Marker::Object => Ref(self.read_object()?),
Marker::Array => Ref(self.read_array()?),
Marker::Enum => Ref(self.read_enum()?),
Marker::ClassDesc => Ref(self.read_class_desc()?),
Marker::ProxyClassDesc => Ref(self.read_proxy_class()?),
Marker::JavaString => Ref(self.read_java_string()?),
Marker::LongString => Ref(self.read_long_string()?),
Marker::Class => Ref(self.read_class()?), Marker::BlockData => BlockData(self.read_block_data()?),
Marker::BlockDataLong => BlockData(self.read_block_data_long()?),
Marker::EndBlockData => EndBlockData,
Marker::Exception => Ref(self.read_exception()?),
Marker::Reset => {
self.reset();
self.read_from_stream()? }
})
}
fn read_reference(&mut self) -> ReadResult<Handle> {
self.stream.read_u32()
}
fn read_class_desc(&mut self) -> ReadResult<Handle> {
let class_name = self.stream.read_string()?;
let serial_uid = self.stream.read_u64()?;
let handle = self.next_handle();
let flags = self.stream.read_u8()?.try_into()?;
let field_count = self.stream.read_u16()?;
let mut fields = Vec::with_capacity(field_count as usize);
for _ in 0..field_count {
let type_code = self.stream.read_u8()? as char;
let field_name = self.stream.read_string()?;
fields.push(if type_code == 'L' || type_code == '[' {
match self.read_from_stream()? {
StreamRecord::Ref(_) => FieldSpec {
name: field_name,
type_spec: type_code,
},
_ => return Err(StreamError::InvalidReference("Expected reference handle")),
}
} else {
FieldSpec {
name: field_name,
type_spec: type_code,
}
});
}
let annotations = self.read_annotations()?;
let super_class = match self.read_from_stream()? {
StreamRecord::Ref(NULL_HANDLE) => None,
StreamRecord::Ref(hnd) => Some(hnd),
_ => {
return Err(StreamError::InvalidStream(
"Super class is neither NewClassDesc nor null",
))
}
};
let class_outline = ClassOutline {
class_name,
fields,
_serial_uid: serial_uid,
_annotations: annotations,
super_class,
flags,
};
self.register(&handle, Reference::Class(class_outline));
Ok(handle)
}
fn read_proxy_class(&mut self) -> ReadResult<Handle> {
let handle = self.next_handle();
let interface_count = self.stream.read_u32()? as i32;
let mut interfaces = vec![];
for _ in 0..interface_count {
interfaces.push(self.stream.read_string()?);
}
let annotations = self.read_annotations()?;
let class_outline = *self.read_from_stream()?.handle()?;
let proxy = Reference::Proxy(ProxyOutline {
_interfaces: interfaces,
_annotations: annotations,
class_outline,
});
self.register(&handle, proxy);
Ok(handle)
}
fn read_annotations(&mut self) -> ReadResult<Vec<Annotation>> {
let mut annotations = vec![];
use Annotation as A;
use StreamRecord::*;
loop {
match self.read_from_stream()? {
Ref(hnd) => annotations.push(A::Ref(hnd)),
BlockData(data) => annotations.push(A::Block(data)),
EndBlockData => break,
};
}
Ok(annotations)
}
fn read_object(&mut self) -> ReadResult<Handle> {
let class_desc = self.read_from_stream()?;
let handle = self.next_handle();
let order = self.build_read_list(class_desc.handle()?)?;
let mut fields = HashMap::new();
let mut annotations = vec![];
for read in order {
match read {
ReadOrder::Fields(spec) => {
for f in spec {
fields.insert(f.name, self.read_value(f.type_spec)?);
}
}
ReadOrder::Annotations => annotations.push(self.read_annotations()?),
}
}
let new_obj = Reference::Object(JavaObject {
class: *class_desc.handle()?,
fields,
annotations,
});
self.register(&handle, new_obj);
Ok(handle)
}
fn build_read_list(&mut self, hnd: &Handle) -> ReadResult<Vec<ReadOrder>> {
let class_desc = self.class_from(hnd)?;
if class_desc.flags == Flag::ExtBlock {
return Ok(vec![ReadOrder::Annotations]);
}
let mut order = match class_desc.super_class {
Some(h) => self.build_read_list(&h)?,
None => vec![],
};
order.push(ReadOrder::Fields(class_desc.fields));
if class_desc.flags == Flag::Write {
order.push(ReadOrder::Annotations);
}
Ok(order)
}
fn read_java_string(&mut self) -> ReadResult<Handle> {
let handle = self.next_handle();
let text = self.stream.read_string()?;
self.register(&handle, Reference::JavaString(text));
Ok(handle)
}
fn read_long_string(&mut self) -> ReadResult<Handle> {
let handle = self.next_handle();
let text = self.stream.read_long_string()?;
self.register(&handle, Reference::JavaString(text));
Ok(handle)
}
fn read_array(&mut self) -> ReadResult<Handle> {
let class_handle = self.read_from_stream()?;
let handle = self.next_handle();
let len = self.stream.read_u32()? as i32;
let new_array = match self
.class_from(class_handle.handle()?)?
.class_name
.chars()
.nth(1)
{
Some('L') | Some('[') => {
let mut data = vec![];
for _ in 0..len {
data.push(*self.read_from_stream()?.handle()?);
}
Reference::Array(data)
}
Some(x) => {
let mut data = vec![];
for _ in 0..len {
data.push(self.read_primitive(x)?);
}
Reference::PrimitiveArray(data)
}
None => return Err(StreamError::UnrecognisedType(' ')),
};
self.register(&handle, new_array);
Ok(handle)
}
fn read_value(&mut self, type_code: char) -> ReadResult<Field> {
use Field::*;
Ok(match type_code {
'L' | '[' => match self.read_from_stream()? {
StreamRecord::Ref(obj_handle) => match self.get_from_handle(&obj_handle) {
Ok(_) => Field::Reference(obj_handle),
Err(_) => Field::Loop(
self.handle_stack
.iter()
.position(|i| i == &obj_handle)
.ok_or(StreamError::UnknownReference(obj_handle))?
as u32,
),
},
_ => return Err(StreamError::InvalidReference("Object")),
},
c => Primitive(self.read_primitive(c)?),
})
}
fn read_primitive(&mut self, type_code: char) -> ReadResult<PrimitiveType> {
use PrimitiveType::*;
Ok(match type_code {
'C' => {
let c = self.stream.read_u16()? as u32;
Char(
std::char::from_u32(c)
.ok_or(StreamError::InvalidStream("invalid character"))?,
)
}
'B' => Byte(self.stream.read_u8()?),
'S' => Short(self.stream.read_u16()? as i16),
'I' => Int(self.stream.read_u32()? as i32),
'J' => Long(self.stream.read_u64()? as i64),
'F' => Float(f32::from_bits(self.stream.read_u32()?)),
'D' => Double(f64::from_bits(self.stream.read_u64()?)),
'Z' => Boolean(self.stream.read_u8()? == 1),
x => return Err(StreamError::UnrecognisedType(x)),
})
}
fn read_class(&mut self) -> ReadResult<Handle> {
let desc = self.read_from_stream()?;
let handle = self.next_handle();
let new_class = Reference::ClassObject(*desc.handle()?);
self.register(&handle, new_class);
Ok(handle)
}
fn read_enum(&mut self) -> ReadResult<Handle> {
let class_desc = self.read_from_stream()?;
let handle = self.next_handle();
let constant_name = self.read_from_stream()?;
let new_enum = Reference::Enum(*class_desc.handle()?, *constant_name.handle()?);
self.register(&handle, new_enum);
Ok(handle)
}
fn read_block_data(&mut self) -> ReadResult<Vec<u8>> {
let len = self.stream.read_u8()?;
let mut data = vec![];
for _ in 0..len {
data.push(self.stream.read_u8()?);
}
Ok(data)
}
fn reset(&mut self) {
self.handle = INITIAL_HANDLE;
self.references.clear();
}
fn read_block_data_long(&mut self) -> ReadResult<Vec<u8>> {
let len = self.stream.read_u32()?;
let mut data = vec![];
for _ in 0..len {
data.push(self.stream.read_u8()?);
}
Ok(data)
}
fn read_exception(&mut self) -> ReadResult<Handle> {
self.reset();
let exception = *self.read_from_stream()?.handle()?;
self.reset();
Ok(exception)
}
fn get_from_handle(&self, handle: &u32) -> ReadResult<&Reference> {
if handle == &NULL_HANDLE {
Ok(&Reference::Null)
} else {
match self.references.get(handle) {
Some(refn) => Ok(refn),
None => Err(StreamError::UnknownReference(*handle)),
}
}
}
fn register(&mut self, handle: &u32, reference: Reference) {
if self.handle_stack.pop() != Some(*handle) {
panic!("object was registered before something it references");
}
self.references.insert(*handle, reference);
}
fn class_from(&self, hnd: &Handle) -> ReadResult<ClassOutline> {
let reference = self.get_from_handle(hnd)?;
match reference {
Reference::Proxy(proxy) => Ok(self.class_from(&proxy.class_outline)?),
Reference::Class(outline) => Ok(outline.clone()),
_ => Err(StreamError::InvalidReference("Class")),
}
}
fn value_from_reference(&self, hnd: &Handle) -> ReadResult<Value> {
use Reference::*;
use Value as V;
Ok(match self.get_from_handle(hnd)? {
Null => V::Null,
PrimitiveArray(data) => V::PrimitiveArray(data.to_vec()),
Array(data) => V::Array(
data.iter()
.map(|h| self.value_from_reference(h))
.collect::<ReadResult<_>>()?,
),
JavaString(s) => V::JavaString(s.to_string()),
Enum(class, cons) => {
let outline = self.get_from_handle(class)?.class_outline()?;
let constant = self.get_from_handle(cons)?.as_string()?;
V::Enum(outline.class_name.to_string(), constant.to_string())
}
ClassObject(h) => V::Class(
self.get_from_handle(h)?
.class_outline()?
.class_name
.to_string(),
),
Object(obj) => {
let class = self.class_from(&obj.class)?;
let mut field_data = HashMap::new();
for (name, field) in &obj.fields {
field_data.insert(
name.to_string(),
match field {
Field::Loop(l) => Value::Loop(-(*l as i32)),
Field::Reference(h) => self.value_from_reference(h)?,
Field::Primitive(p) => Value::Primitive(*p),
},
);
}
let mut anno_data = vec![];
for class_anno in &obj.annotations {
anno_data.push(
class_anno
.iter()
.map(|anno| {
Ok(match anno {
Annotation::Ref(h) => {
Content::Object(self.value_from_reference(h)?)
}
Annotation::Block(data) => Content::Block(data.to_vec()),
})
})
.collect::<ReadResult<Vec<_>>>()?,
);
}
V::Object(ObjectData {
class: class.class_name,
fields: field_data,
annotations: anno_data,
})
}
_ => return Err(StreamError::NotImplemented("value from reference")),
})
}
pub fn read(&mut self) -> ReadResult<Content> {
use StreamRecord::*;
Ok(match self.read_from_stream()? {
Ref(hnd) => Content::Object(self.value_from_reference(&hnd)?),
EndBlockData => return Err(StreamError::InvalidStream("Unexpected EndBlockData mark")),
BlockData(data) => Content::Block(data),
})
}
pub fn read_as<S: FromJava>(&mut self) -> Result<S> {
match self.read()? {
Content::Object(value) => Ok(S::from_value(&value)?),
Content::Block(data) => Err(JavaError::ConvertError(
ConversionError::UnexpectedBlockData(data),
)),
}
}
}
#[derive(Debug)]
enum StreamRecord {
EndBlockData,
BlockData(Vec<u8>),
Ref(u32),
}
impl StreamRecord {
fn handle(&self) -> ReadResult<&Handle> {
match self {
StreamRecord::Ref(hnd) => Ok(hnd),
_ => Err(StreamError::InvalidReference("Ref")),
}
}
}
#[derive(Debug)]
enum Reference {
Class(ClassOutline),
Proxy(ProxyOutline),
Null,
JavaString(String),
Array(Vec<Handle>),
PrimitiveArray(Vec<PrimitiveType>),
ClassObject(Handle), Object(JavaObject),
Enum(Handle, Handle), }
impl Reference {
fn as_string(&self) -> ReadResult<&str> {
match self {
Self::JavaString(text) => Ok(text),
_ => Err(StreamError::InvalidReference("String")),
}
}
fn class_outline(&self) -> ReadResult<&ClassOutline> {
match self {
Self::Class(outline) => Ok(outline),
_ => Err(StreamError::InvalidReference("ClassOutline")),
}
}
}
#[derive(Debug, Clone)]
struct ClassOutline {
class_name: String,
_serial_uid: u64,
super_class: Option<Handle>,
fields: Vec<FieldSpec>,
_annotations: Vec<Annotation>,
flags: Flag,
}
#[derive(Debug)]
struct ProxyOutline {
_interfaces: Vec<String>,
_annotations: Vec<Annotation>,
class_outline: Handle, }
#[derive(Debug, Clone)]
struct FieldSpec {
name: String,
type_spec: char,
}
#[derive(Debug)]
enum ReadOrder {
Fields(Vec<FieldSpec>),
Annotations,
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PrimitiveType {
Byte(u8),
Char(char),
Double(f64),
Float(f32),
Int(i32),
Long(i64),
Short(i16),
Boolean(bool),
}
impl Display for PrimitiveType {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Self::Byte(b) => write!(f, "{b:X}")?,
Self::Char(c) => write!(f, "{c}")?,
Self::Double(d) => write!(f, "{d}")?,
Self::Float(fl) => write!(f, "{fl}")?,
Self::Int(i) => write!(f, "{i}")?,
Self::Short(s) => write!(f, "{s}")?,
Self::Long(l) => write!(f, "{l}")?,
Self::Boolean(b) => write!(f, "{b}")?,
}
Ok(())
}
}
#[derive(Debug)]
struct JavaObject {
class: Handle,
fields: HashMap<String, Field>,
annotations: Vec<Vec<Annotation>>, }
#[derive(Debug, Clone)]
enum Annotation {
Ref(Handle),
Block(Vec<u8>),
}
#[derive(Debug)]
enum Field {
Primitive(PrimitiveType),
Reference(Handle),
Loop(Handle),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ObjectData {
class: String,
fields: HashMap<String, Value>,
annotations: Vec<Vec<Content>>, }
impl ObjectData {
pub fn class_name(&self) -> &str {
&self.class
}
pub fn get_field(&self, name: &str) -> Option<&Value> {
self.fields.get(name)
}
pub fn get_field_as<T: FromJava>(&self, name: &str) -> ConversionResult<T> {
match self.get_field(name) {
Some(v) => Ok(T::from_value(v)?),
None => Err(ConversionError::FieldNotFound(name.to_string())),
}
}
pub fn get_annotation(&self, ind: usize) -> Option<annotations::AnnotationIter> {
self.annotations
.get(ind)
.map(|anno| annotations::AnnotationIter::new(anno))
}
pub fn annotation_count(&self) -> usize {
self.annotations.iter().filter(|a| !a.is_empty()).count()
}
pub fn field_count(&self) -> usize {
self.fields.len()
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Value {
Null,
Object(ObjectData),
JavaString(String),
Enum(String, String), Primitive(PrimitiveType),
Array(Vec<Value>),
PrimitiveArray(Vec<PrimitiveType>),
Class(String),
Loop(i32),
}
impl Value {
pub fn primitive(&self) -> Option<&PrimitiveType> {
match self {
Value::Primitive(pt) => Some(pt),
_ => None,
}
}
pub fn array(&self) -> Option<&[Value]> {
match self {
Value::Array(values) => Some(values),
_ => None,
}
}
pub fn primitive_array(&self) -> Option<&[PrimitiveType]> {
match self {
Value::PrimitiveArray(values) => Some(values),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn string(&self) -> Option<&str> {
match self {
Value::JavaString(s) => Some(s),
_ => None,
}
}
pub fn object_data(&self) -> Option<&ObjectData> {
match self {
Value::Object(obj) => Some(obj),
_ => None,
}
}
pub fn enum_data(&self) -> Option<(&str, &str)> {
match self {
Value::Enum(cls, cons) => Some((cls, cons)),
_ => None,
}
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Content {
Object(Value),
Block(Vec<u8>),
}
impl Content {
pub fn value(&self) -> Option<&Value> {
match self {
Content::Object(val) => Some(val),
_ => None,
}
}
pub fn data(&self) -> Option<&[u8]> {
match self {
Content::Block(data) => Some(data),
_ => None,
}
}
}
#[cfg(test)]
mod parser_tests {
use super::StreamRecord::*;
use super::*;
#[test]
fn invalid_stream() {
let dat: &[u8] = &[0xAC, 0xDE, 0x00, 0x05];
let parser = Parser::new(dat);
match parser {
Ok(_) => panic!("Parser shouldn't be created with invalid magic marker"),
Err(JavaError::ReadError(StreamError::NonJavaObject(mm))) => assert_eq!(
mm, 0xACDE,
"NonJavaObject error has wrong magic marker data",
),
Err(e) => panic!("Error should have been NonJavaObject but was '{}'", e),
}
}
#[test]
fn invalid_stream_version() {
let dat: &[u8] = &[0xAC, 0xED, 0x00, 0x06];
let parser = Parser::new(dat);
match parser {
Ok(_) => panic!("Parser shouldn't be created with unknown version"),
Err(JavaError::ReadError(StreamError::UnknownVersion(version))) => {
assert_eq!(version, 6, "UnknownVersion error has wrong version",)
}
Err(e) => panic!("Error should have been UnknownVersion but was '{}'", e),
}
}
#[test]
fn correct_marker_and_version() {
let dat: &[u8] = &[0xAC, 0xED, 0x00, 0x05];
let parser = Parser::new(dat);
let parser = parser.expect("Parser should be created with valid stream");
assert_eq!(parser._version, 5, "Parser has incorrect version");
assert_eq!(
parser.handle, INITIAL_HANDLE,
"Parser started with incorrect handle"
);
}
#[test]
fn handles_are_incremented() {
let dat: &[u8] = &[0xAC, 0xED, 0x00, 0x05];
let mut parser = Parser::new(dat).expect("Parser failed to initialise");
assert_eq!(parser.next_handle(), INITIAL_HANDLE);
assert_eq!(parser.next_handle(), INITIAL_HANDLE + 1);
assert_eq!(parser.next_handle(), INITIAL_HANDLE + 2);
}
#[test]
fn handle_stack_holds_current_handles() {
let dat: &[u8] = &[0xAC, 0xED, 0x00, 0x05];
let mut parser = Parser::new(dat).expect("Parser failed to initialise");
let a = parser.next_handle();
let b = parser.next_handle();
assert_eq!(vec![a, b], parser.handle_stack);
parser.register(&b, Reference::Null);
assert_eq!(vec![a], parser.handle_stack);
}
#[test]
#[should_panic(expected = "object was registered before something it references")]
fn handles_must_be_registered_in_order() {
let dat: &[u8] = &[0xAC, 0xED, 0x00, 0x05];
let mut parser = Parser::new(dat).expect("Parser failed to initialise");
let a = parser.next_handle();
let _b = parser.next_handle();
parser.register(&a, Reference::Null);
}
#[test]
fn read_null_reference() -> Result<()> {
let dat: &[u8] = &[
0xAC, 0xED, 0x00, 0x05, 0x70, ];
let mut parser = Parser::new(dat)?;
let result = parser.read_from_stream()?;
match result {
BlockData(_) => panic!("Expected null reference but read block data"),
EndBlockData => panic!("Expected null reference but read end block data"),
Ref(0) => (),
Ref(x) => panic!("Expected null reference but read Ref({}) instead", x),
}
assert_eq!(
parser.handle, INITIAL_HANDLE,
"Handle shouldn't be incremented for null"
);
Ok(())
}
#[test]
fn short_string() -> Result<()> {
let dat: &[u8] = &[
0xAC, 0xED, 0x00, 0x05, 0x74, 0x00, 0x0A, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x57, 0x6F, 0x72, 0x6C, 0x64, ];
let mut parser = Parser::new(dat)?;
let result = parser.read_from_stream()?;
match result {
BlockData(_) => panic!("Expected string but read block data"),
EndBlockData => panic!("Expected string but read end block data"),
Ref(x) => {
assert_eq!(x, INITIAL_HANDLE);
let read = parser.get_from_handle(&x)?;
match read {
Reference::JavaString(s) => assert_eq!(s, "helloWorld"),
x => panic!("Expected string but read {:?}", x),
}
}
}
Ok(())
}
#[test]
fn repeated_string() -> Result<()> {
let dat: &[u8] = &[
0xAC, 0xED, 0x00, 0x05, 0x74, 0x00, 0x0A, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x71, 0x00, 0x7E, 0x00, 0x00, ];
let mut parser = Parser::new(dat)?;
let _result = parser.read_from_stream()?;
let second = parser.read_from_stream()?;
match second {
BlockData(_) => panic!("Expected string but read block data"),
EndBlockData => panic!("Expected string but read end block data"),
Ref(x) => {
assert_eq!(x, INITIAL_HANDLE);
let read = parser.get_from_handle(&x)?;
match read {
Reference::JavaString(s) => assert_eq!(s, "helloWorld"),
x => panic!("Expected string but read {:?}", x),
}
}
}
Ok(())
}
#[test]
fn read_class_object() -> Result<()> {
let dat: &[u8] = &[
0xAC, 0xED, 0x00, 0x05, 0x76, 0x72, 0x00, 0x11, 0x6A, 0x61, 0x76, 0x61, 0x2E, 0x6C, 0x61, 0x6E, 0x67, 0x2E, 0x49, 0x6E, 0x74, 0x65,
0x67, 0x65, 0x72, 0x12, 0xE2, 0xA0, 0xA4, 0xF7, 0x81, 0x87, 0x38, 0x02, 0x00, 0x01, 0x49, 0x00, 0x05, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x78, 0x72, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2E, 0x6C, 0x61, 0x6E, 0x67, 0x2E, 0x4E, 0x75, 0x6D, 0x62,
0x65, 0x72, 0x86, 0xAC, 0x95, 0x1D, 0x0B, 0x94, 0xE0, 0x8B, 0x02, 0x00, 0x00, 0x78, 0x70, ];
let mut parser = Parser::new(dat)?;
let result = parser.read_from_stream()?;
match result {
BlockData(_) => panic!("Expected Class handle but read block data"),
EndBlockData => panic!("Expected Class handle but read end block data"),
Ref(x) => {
assert_eq!(x, INITIAL_HANDLE + 2); let read = parser.get_from_handle(&x)?;
match read {
Reference::ClassObject(hnd) => {
let outline = parser.get_from_handle(hnd)?.class_outline()?;
assert_eq!(outline.class_name, "java.lang.Integer");
assert!(outline._annotations.is_empty());
assert_eq!(outline.flags, Flag::NoWrite);
let super_class =
parser.get_from_handle(&(outline.super_class.unwrap()))?;
assert_eq!(super_class.class_outline()?.class_name, "java.lang.Number");
}
x => panic!("Expected class object but read {:?}", x),
}
}
}
Ok(())
}
#[test]
fn read_invalid_primitive() -> Result<()> {
let data: &[u8] = &[0xAC, 0xED, 0x00, 0x05, 0x42];
let mut parser = Parser::new(data)?;
match parser.read_primitive('X') {
Ok(p) => panic!("Expected invalid primitive, read '{}'", p),
Err(StreamError::UnrecognisedType(e)) => assert_eq!(e, 'X'),
Err(e) => panic!("Expect unrecognised primitive error, but got '{}'", e),
}
Ok(())
}
}