<commit_before>/// Implements all outgoing status packets

use std::io::Write;

use super::super::super::deque_buffer::DequeBuffer;
use super::super::super::game_connection::GameConnection; //It's super super super effective!
use super::super::{ ID_STATUS_STC_RESPONSE, ID_STATUS_STC_PONG };

use super::OutgoingPacket;

use rustc_serialize::json;

mod response {

    use super::super::super::super::super::super::{get_version, MINECRAFT_PROTOCOL_VERSION};
    use super::super::super::super::super::super::universe::{MAX_PLAYERS};

    /// A response to be sent by the server list response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Response {

        /// The player data contained in this response
        pub players: Players,

        /// The version data contained in this response
        pub version: Version,

        /// The description contained in this response
        pub description: Description,

        /// The optional / Forge-only mod information
        pub modinfo: Option<ModInfo>

    }

    impl Response {

        /// Constructs a new Response
        pub fn construct() -> Response {

            return Response { players: Players::construct(), version: Version::construct(), description: Description::construct(), modinfo: Some(ModInfo::construct())};

        }

    }

    /// A struct containing mod (forge) information
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    #[allow(non_snake_case)]
    pub struct ModInfo {

        /// The type of mod loader compatibility being used
        pub _type: String,

        /// The list of mods currently in use on the server
        pub modList: Vec<ModDescriptor>

    }

    impl ModInfo {

        /// Constructs a new ModInfo struct
        pub fn construct() -> ModInfo {

            ModInfo { _type: "FML".to_string(), modList: ModDescriptor::construct() }

        }

    }

    /// A struct containing mod information
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct ModDescriptor {

        /// The mod's ID
        pub modid: String,

        /// The mod's version
        pub version: String

    }

    impl ModDescriptor {

        /// Constructs a new list of ModDescriptors
        pub fn construct() -> Vec<ModDescriptor> {

            //TODO: One day, replace this with names of mods added
            vec!(
                ModDescriptor { modid: "ExampleMod".to_string(), version: "0.0.1-pre".to_string() }
            )

        }

    }

    /// A struct defining the players portion of the StatusResponsePacket
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Players {

        /// The maximum number of players allowed on this server
        pub max: i32, //Yes, I know. But Java only supports signed integers

        /// The current number of online players
        pub online: i32

    }

    impl Players {

        /// Constructs a new Players struct
        ///
        /// TODO: Make this use real data for online
        pub fn construct() -> Players {

            Players { max: MAX_PLAYERS, online: 0 }

        }

    }

    /// The version part of the response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Version {

        /// The name of the version (This may or may not be ignored)
        pub name: String,

        /// The Minecraft protocol number being used
        pub protocol: u32

    }

    impl Version {

        /// Constructs a new Version struct
        ///
        /// TODO: Make name use a defined value
        pub fn construct() -> Version {

            Version { name: "1.8.8".to_string(), protocol: MINECRAFT_PROTOCOL_VERSION }

        }

    }

    /// The description part of the response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Description {

        /// The text to display
        pub text: String

    }

    impl Description {

        /// Constructs a new Description.
        ///
        /// In the future, this will get the description from a (cached) file
        pub fn construct() -> Description {
            Description { text: format!("A Netherrack {} server", get_version()) }
        }

    }

}

/// A packet sent from the server to
pub struct StatusResponsePacket;

impl StatusResponsePacket {

    /// Gets this StatusResponsePacket's ID
    pub fn get_id(&self) -> u32 {

        ID_STATUS_STC_RESPONSE

    }

    /// Creates a new Packet
    pub fn new() -> StatusResponsePacket {

        StatusResponsePacket

    }

    /// Encodes and sends this StatusResponsePacket
    pub fn send(&self, connection: &mut GameConnection) {

        let mut data_buffer = DequeBuffer::new();

        data_buffer.write_unsigned_varint_32(self.get_id());

        data_buffer.write_utf8_string(json::encode(&response::Response::construct()).unwrap().replace("_type", "type"));

        let mut length_buffer = DequeBuffer::new();

        length_buffer.write_unsigned_varint_32(data_buffer.remaining() as u32); //TODO: Try to get this as write_unsigned_varint_32_front

        let buffer: Vec<u8> = length_buffer.data.into_iter().chain(data_buffer.data.into_iter()).collect();

        let _buffer: &[u8] = &buffer[..];

        if connection.stream.write(_buffer).is_ok() {
            let _ = connection.stream.flush();
        }

    }

}

/// A packet sent from the server to the client responding to a ping
pub struct ListPongPacket {

    pub payload: i64

}

impl ListPongPacket {

    /// Creates a new ListPongPacket
    pub fn new(payload: i64) -> ListPongPacket {

        ListPongPacket { payload: payload }

    }
}

impl OutgoingPacket for ListPongPacket {

    /// Gets this ListPongPacket's ID
    fn get_id(&self) -> u32 {

        ID_STATUS_STC_PONG

    }

    /// Encodes this Packet's data
    fn encode_data(&self, buffer: &mut DequeBuffer) {
        buffer.write_signed_long(self.payload);
    }

    /// Called after the OutgoingPacket is sent
    fn post_send(&self, connection: &mut GameConnection) {
        // All ListPongPackets are at the end of any packet stream, so we can disconnect the client here
        connection.disconnect();
    }

}
<commit_msg>Finish implementing OutgoingPacket for outgoing status packets<commit_after>//! Implements all outgoing status packets

use super::super::super::deque_buffer::DequeBuffer;
use super::super::super::game_connection::GameConnection; //It's super super super effective!
use super::super::{ ID_STATUS_STC_RESPONSE, ID_STATUS_STC_PONG };

use super::OutgoingPacket;

use rustc_serialize::json;

mod response {

    use super::super::super::super::super::super::{get_version, MINECRAFT_PROTOCOL_VERSION};
    use super::super::super::super::super::super::universe::{MAX_PLAYERS};

    /// A response to be sent by the server list response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Response {

        /// The player data contained in this response
        pub players: Players,

        /// The version data contained in this response
        pub version: Version,

        /// The description contained in this response
        pub description: Description,

        /// The optional / Forge-only mod information
        pub modinfo: Option<ModInfo>

    }

    impl Response {

        /// Constructs a new Response
        pub fn construct() -> Response {

            return Response { players: Players::construct(), version: Version::construct(), description: Description::construct(), modinfo: Some(ModInfo::construct())};

        }

    }

    /// A struct containing mod (forge) information
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    #[allow(non_snake_case)]
    pub struct ModInfo {

        /// The type of mod loader compatibility being used
        pub _type: String,

        /// The list of mods currently in use on the server
        pub modList: Vec<ModDescriptor>

    }

    impl ModInfo {

        /// Constructs a new ModInfo struct
        pub fn construct() -> ModInfo {

            ModInfo { _type: "FML".to_string(), modList: ModDescriptor::construct() }

        }

    }

    /// A struct containing mod information
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct ModDescriptor {

        /// The mod's ID
        pub modid: String,

        /// The mod's version
        pub version: String

    }

    impl ModDescriptor {

        /// Constructs a new list of ModDescriptors
        pub fn construct() -> Vec<ModDescriptor> {

            //TODO: One day, replace this with names of mods added
            vec!(
                ModDescriptor { modid: "ExampleMod".to_string(), version: "0.0.1-pre".to_string() }
            )

        }

    }

    /// A struct defining the players portion of the StatusResponsePacket
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Players {

        /// The maximum number of players allowed on this server
        pub max: i32, //Yes, I know. But Java only supports signed integers

        /// The current number of online players
        pub online: i32

    }

    impl Players {

        /// Constructs a new Players struct
        ///
        /// TODO: Make this use real data for online
        pub fn construct() -> Players {

            Players { max: MAX_PLAYERS, online: 0 }

        }

    }

    /// The version part of the response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Version {

        /// The name of the version (This may or may not be ignored)
        pub name: String,

        /// The Minecraft protocol number being used
        pub protocol: u32

    }

    impl Version {

        /// Constructs a new Version struct
        ///
        /// TODO: Make name use a defined value
        pub fn construct() -> Version {

            Version { name: "1.8.8".to_string(), protocol: MINECRAFT_PROTOCOL_VERSION }

        }

    }

    /// The description part of the response packet
    #[derive(Debug, RustcDecodable, RustcEncodable)]
    pub struct Description {

        /// The text to display
        pub text: String

    }

    impl Description {

        /// Constructs a new Description.
        ///
        /// In the future, this will get the description from a (cached) file
        pub fn construct() -> Description {
            Description { text: format!("A Netherrack {} server", get_version()) }
        }

    }

}

/// A packet sent from the server to
pub struct StatusResponsePacket;

impl StatusResponsePacket {

    /// Creates a new Packet
    pub fn new() -> StatusResponsePacket {

        StatusResponsePacket

    }

}

impl OutgoingPacket for StatusResponsePacket {

    /// Gets this StatusResponsePacket's ID
    ///
    /// One day, this will be replaced with an associated constant
    fn get_id(&self) -> u32 {
        ID_STATUS_STC_RESPONSE
    }

    /// Encodes this StatusResponsePacket's data
    fn encode_data(&self, buffer: &mut DequeBuffer) {
        buffer.write_utf8_string(
            json::encode(&response::Response::construct()).unwrap().replace("_type", "type")
        );
    }

}

/// A packet sent from the server to the client responding to a ping
pub struct ListPongPacket {

    pub payload: i64

}

impl ListPongPacket {

    /// Creates a new ListPongPacket
    pub fn new(payload: i64) -> ListPongPacket {

        ListPongPacket { payload: payload }

    }
}

impl OutgoingPacket for ListPongPacket {

    /// Gets this ListPongPacket's ID
    fn get_id(&self) -> u32 {

        ID_STATUS_STC_PONG

    }

    /// Encodes this Packet's data
    fn encode_data(&self, buffer: &mut DequeBuffer) {
        buffer.write_signed_long(self.payload);
    }

    /// Called after the OutgoingPacket is sent
    fn post_send(&self, connection: &mut GameConnection) {
        // All ListPongPackets are at the end of any packet stream, so we can disconnect the client here
        connection.disconnect();
    }

}
<|endoftext|>


<commit_before>//! Generate binary emission code for each ISA.

use cranelift_entity::EntityRef;

use crate::error;
use crate::srcgen::Formatter;

use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes};

/// Generate code to handle a single recipe.
///
/// - Unpack the instruction data, knowing the format.
/// - Determine register locations for operands with register constraints.
/// - Determine stack slot locations for operands with stack constraints.
/// - Call hand-written code for the actual emission.
fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Formatter) {
    let inst_format = formats.get(recipe.format);
    let num_value_ops = inst_format.num_value_operands;

    let want_args = recipe.operands_in.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });
    assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);

    let want_outs = recipe.operands_out.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });

    let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name);

    // Unpack the instruction data.
    fmtln!(fmt, "if let InstructionData::{} {{", inst_format.name);
    fmt.indent(|fmt| {
        fmt.line("opcode,");
        for f in &inst_format.imm_fields {
            fmtln!(fmt, "{},", f.member);
        }
        if want_args {
            if inst_format.has_value_list || num_value_ops > 1 {
                fmt.line("ref args,");
            } else {
                fmt.line("arg,");
            }
        }
        fmt.line("..");

        fmt.outdented_line("} = func.dfg[inst] {");

        // Pass recipe arguments in this order: inputs, imm_fields, outputs.
        let mut args = String::new();

        if want_args && !is_regmove {
            if inst_format.has_value_list {
                fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
            } else if num_value_ops == 1 {
                fmt.line("let args = [arg];");
            }
            args += &unwrap_values(&recipe.operands_in, "in", "args", fmt);
        }

        for f in &inst_format.imm_fields {
            args += &format!(", {}", f.member);
        }

        // Unwrap interesting output arguments.
        if want_outs {
            if recipe.operands_out.len() == 1 {
                fmt.line("let results = [func.dfg.first_result(inst)];")
            } else {
                fmt.line("let results = func.dfg.inst_results(inst);");
            }
            args += &unwrap_values(&recipe.operands_out, "out", "results", fmt);
        }

        // Special handling for regmove instructions. Update the register
        // diversion tracker
        match &*inst_format.name {
            "RegMove" => fmt.line("divert.regmove(arg, src, dst);"),
            "RegSpill" => fmt.line("divert.regspill(arg, src, dst);"),
            "RegFill" => fmt.line("divert.regfill(arg, src, dst);"),
            _ => {}
        }

        match &recipe.emit {
            Some(emit) => {
                fmt.multi_line(emit);
                fmt.line("return;");
            }
            None => {
                fmtln!(
                    fmt,
                    "return recipe_{}(func, inst, sink, bits{});",
                    recipe.name.to_lowercase(),
                    args
                );
            }
        }
    });
    fmt.line("}");
}

/// Emit code that unwraps values living in registers or stack slots.
///
/// :param args: Input or output constraints.
/// :param prefix: Prefix to be used for the generated local variables.
/// :param values: Name of slice containing the values to be unwrapped.
/// :returns: Comma separated list of the generated variables
fn unwrap_values(
    args: &[OperandConstraint],
    prefix: &str,
    values_slice: &str,
    fmt: &mut Formatter,
) -> String {
    let mut varlist = String::new();
    for (i, cst) in args.iter().enumerate() {
        match cst {
            OperandConstraint::RegClass(_reg_class) => {
                let v = format!("{}_reg{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(
                    fmt,
                    "let {} = divert.reg({}[{}], &func.locations);",
                    v,
                    values_slice,
                    i
                );
            }
            OperandConstraint::Stack(stack) => {
                let v = format!("{}_stk{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(fmt, "let {} = StackRef::masked(", v);
                fmt.indent(|fmt| {
                    fmtln!(
                        fmt,
                        "divert.stack({}[{}], &func.locations),",
                        values_slice,
                        i
                    );
                    fmt.line(format!("{}, ", stack.stack_base_mask()));
                    fmt.line("&func.stack_slots,");
                });
                fmt.line(").unwrap();");
            }
            _ => {}
        }
    }
    varlist
}

fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) {
    fmt.doc_comment(format!(
        "Emit binary machine code for `inst` for the {} ISA.",
        isa_name
    ));

    if recipes.is_empty() {
        fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
        fmt.indent(|fmt| {
            fmt.line("func: &Function,");
            fmt.line("inst: Inst,");
            fmt.line("_divert: &mut RegDiversions,");
            fmt.line("_sink: &mut CS,");
        });
        fmt.line(") {");
        fmt.indent(|fmt| {
            // No encoding recipes: Emit a stub.
            fmt.line("bad_encoding(func, inst)");
        });
        fmt.line("}");
        return;
    }

    fmt.line("#[allow(unused_variables, unreachable_code)]");
    fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
    fmt.indent(|fmt| {
        fmt.line("func: &Function,");
        fmt.line("inst: Inst,");
        fmt.line("divert: &mut RegDiversions,");
        fmt.line("sink: &mut CS,");
    });

    fmt.line(") {");
    fmt.indent(|fmt| {
        fmt.line("let encoding = func.encodings[inst];");
        fmt.line("let bits = encoding.bits();");
        fmt.line("match func.encodings[inst].recipe() {");
        fmt.indent(|fmt| {
            for (i, recipe) in recipes.iter() {
                fmt.comment(format!("Recipe {}", recipe.name));
                fmtln!(fmt, "{} => {{", i.index());
                fmt.indent(|fmt| {
                    gen_recipe(formats, recipe, fmt);
                });
                fmt.line("}");
            }
            fmt.line("_ => {},");
        });
        fmt.line("}");

        // Allow for unencoded ghost instructions. The verifier will check details.
        fmt.line("if encoding.is_legal() {");
        fmt.indent(|fmt| {
            fmt.line("bad_encoding(func, inst);");
        });
        fmt.line("}");
    });
    fmt.line("}");
}

pub fn generate(
    formats: &FormatRegistry,
    isa_name: &str,
    recipes: &Recipes,
    binemit_filename: &str,
    out_dir: &str,
) -> Result<(), error::Error> {
    let mut fmt = Formatter::new();
    gen_isa(formats, isa_name, recipes, &mut fmt);
    fmt.update_file(binemit_filename, out_dir)?;
    Ok(())
}
<commit_msg>Factor out func.dfg[inst] accesses in binemit.<commit_after>//! Generate binary emission code for each ISA.

use cranelift_entity::EntityRef;

use crate::error;
use crate::srcgen::Formatter;

use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::recipes::{EncodingRecipe, OperandConstraint, Recipes};

/// Generate code to handle a single recipe.
///
/// - Unpack the instruction data, knowing the format.
/// - Determine register locations for operands with register constraints.
/// - Determine stack slot locations for operands with stack constraints.
/// - Call hand-written code for the actual emission.
fn gen_recipe(formats: &FormatRegistry, recipe: &EncodingRecipe, fmt: &mut Formatter) {
    let inst_format = formats.get(recipe.format);
    let num_value_ops = inst_format.num_value_operands;

    let want_args = recipe.operands_in.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });
    assert!(!want_args || num_value_ops > 0 || inst_format.has_value_list);

    let want_outs = recipe.operands_out.iter().any(|c| match c {
        OperandConstraint::RegClass(_) | OperandConstraint::Stack(_) => true,
        OperandConstraint::FixedReg(_) | OperandConstraint::TiedInput(_) => false,
    });

    let is_regmove = ["RegMove", "RegSpill", "RegFill"].contains(&inst_format.name);

    // Unpack the instruction data.
    fmtln!(fmt, "if let &InstructionData::{} {{", inst_format.name);
    fmt.indent(|fmt| {
        fmt.line("opcode,");
        for f in &inst_format.imm_fields {
            fmtln!(fmt, "{},", f.member);
        }
        if want_args {
            if inst_format.has_value_list || num_value_ops > 1 {
                fmt.line("ref args,");
            } else {
                fmt.line("arg,");
            }
        }
        fmt.line("..");

        fmt.outdented_line("} = data {");

        // Pass recipe arguments in this order: inputs, imm_fields, outputs.
        let mut args = String::new();

        if want_args && !is_regmove {
            if inst_format.has_value_list {
                fmt.line("let args = args.as_slice(&func.dfg.value_lists);");
            } else if num_value_ops == 1 {
                fmt.line("let args = [arg];");
            }
            args += &unwrap_values(&recipe.operands_in, "in", "args", fmt);
        }

        for f in &inst_format.imm_fields {
            args += &format!(", {}", f.member);
        }

        // Unwrap interesting output arguments.
        if want_outs {
            if recipe.operands_out.len() == 1 {
                fmt.line("let results = [func.dfg.first_result(inst)];")
            } else {
                fmt.line("let results = func.dfg.inst_results(inst);");
            }
            args += &unwrap_values(&recipe.operands_out, "out", "results", fmt);
        }

        // Special handling for regmove instructions. Update the register
        // diversion tracker
        match &*inst_format.name {
            "RegMove" => fmt.line("divert.regmove(arg, src, dst);"),
            "RegSpill" => fmt.line("divert.regspill(arg, src, dst);"),
            "RegFill" => fmt.line("divert.regfill(arg, src, dst);"),
            _ => {}
        }

        match &recipe.emit {
            Some(emit) => {
                fmt.multi_line(emit);
                fmt.line("return;");
            }
            None => {
                fmtln!(
                    fmt,
                    "return recipe_{}(func, inst, sink, bits{});",
                    recipe.name.to_lowercase(),
                    args
                );
            }
        }
    });
    fmt.line("}");
}

/// Emit code that unwraps values living in registers or stack slots.
///
/// :param args: Input or output constraints.
/// :param prefix: Prefix to be used for the generated local variables.
/// :param values: Name of slice containing the values to be unwrapped.
/// :returns: Comma separated list of the generated variables
fn unwrap_values(
    args: &[OperandConstraint],
    prefix: &str,
    values_slice: &str,
    fmt: &mut Formatter,
) -> String {
    let mut varlist = String::new();
    for (i, cst) in args.iter().enumerate() {
        match cst {
            OperandConstraint::RegClass(_reg_class) => {
                let v = format!("{}_reg{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(
                    fmt,
                    "let {} = divert.reg({}[{}], &func.locations);",
                    v,
                    values_slice,
                    i
                );
            }
            OperandConstraint::Stack(stack) => {
                let v = format!("{}_stk{}", prefix, i);
                varlist += &format!(", {}", v);
                fmtln!(fmt, "let {} = StackRef::masked(", v);
                fmt.indent(|fmt| {
                    fmtln!(
                        fmt,
                        "divert.stack({}[{}], &func.locations),",
                        values_slice,
                        i
                    );
                    fmt.line(format!("{}, ", stack.stack_base_mask()));
                    fmt.line("&func.stack_slots,");
                });
                fmt.line(").unwrap();");
            }
            _ => {}
        }
    }
    varlist
}

fn gen_isa(formats: &FormatRegistry, isa_name: &str, recipes: &Recipes, fmt: &mut Formatter) {
    fmt.doc_comment(format!(
        "Emit binary machine code for `inst` for the {} ISA.",
        isa_name
    ));

    if recipes.is_empty() {
        fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
        fmt.indent(|fmt| {
            fmt.line("func: &Function,");
            fmt.line("inst: Inst,");
            fmt.line("_divert: &mut RegDiversions,");
            fmt.line("_sink: &mut CS,");
        });
        fmt.line(") {");
        fmt.indent(|fmt| {
            // No encoding recipes: Emit a stub.
            fmt.line("bad_encoding(func, inst)");
        });
        fmt.line("}");
        return;
    }

    fmt.line("#[allow(unused_variables, unreachable_code)]");
    fmt.line("pub fn emit_inst<CS: CodeSink + ?Sized>(");
    fmt.indent(|fmt| {
        fmt.line("func: &Function,");
        fmt.line("inst: Inst,");
        fmt.line("divert: &mut RegDiversions,");
        fmt.line("sink: &mut CS,");
    });

    fmt.line(") {");
    fmt.indent(|fmt| {
        fmt.line("let encoding = func.encodings[inst];");
        fmt.line("let bits = encoding.bits();");
        fmt.line("let data = &func.dfg[inst];");
        fmt.line("match encoding.recipe() {");
        fmt.indent(|fmt| {
            for (i, recipe) in recipes.iter() {
                fmt.comment(format!("Recipe {}", recipe.name));
                fmtln!(fmt, "{} => {{", i.index());
                fmt.indent(|fmt| {
                    gen_recipe(formats, recipe, fmt);
                });
                fmt.line("}");
            }
            fmt.line("_ => {},");
        });
        fmt.line("}");

        // Allow for unencoded ghost instructions. The verifier will check details.
        fmt.line("if encoding.is_legal() {");
        fmt.indent(|fmt| {
            fmt.line("bad_encoding(func, inst);");
        });
        fmt.line("}");
    });
    fmt.line("}");
}

pub fn generate(
    formats: &FormatRegistry,
    isa_name: &str,
    recipes: &Recipes,
    binemit_filename: &str,
    out_dir: &str,
) -> Result<(), error::Error> {
    let mut fmt = Formatter::new();
    gen_isa(formats, isa_name, recipes, &mut fmt);
    fmt.update_file(binemit_filename, out_dir)?;
    Ok(())
}
<|endoftext|>


<commit_before>use rustc_target::spec::abi::{Abi};
use syntax::ast;
use syntax::symbol::sym;
use syntax_pos::Span;

use rustc::ty::{self, TyCtxt};
use rustc::hir::def_id::DefId;
use rustc::mir::{self, Body, Location};
use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};

use crate::dataflow::{do_dataflow, DebugFormatted};
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::BitDenotation;
use crate::dataflow::DataflowResults;
use crate::dataflow::{
    DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
};
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
use crate::dataflow::move_paths::{HasMoveData, MoveData};

use crate::dataflow::has_rustc_mir_with;

pub struct SanityCheck;

impl<'tcx> MirPass<'tcx> for SanityCheck {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = src.def_id();
        if !tcx.has_attr(def_id, sym::rustc_mir) {
            debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
            return;
        } else {
            debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
        }

        let attributes = tcx.get_attrs(def_id);
        let param_env = tcx.param_env(def_id);
        let move_data = MoveData::gather_moves(body, tcx).unwrap();
        let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
        let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
        let flow_inits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        MaybeInitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
        let flow_uninits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        MaybeUninitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
        let flow_def_inits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        DefinitelyInitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));

        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
        }
        if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
            tcx.sess.fatal("stop_after_dataflow ended compilation");
        }
    }
}

/// This function scans `mir` for all calls to the intrinsic
/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
///
/// For each such call, determines what the dataflow bit-state is for
/// the L-value corresponding to `expr`; if the bit-state is a 1, then
/// that call to `rustc_peek` is ignored by the sanity check. If the
/// bit-state is a 0, then this pass emits a error message saying
/// "rustc_peek: bit not set".
///
/// The intention is that one can write unit tests for dataflow by
/// putting code into a compile-fail test and using `rustc_peek` to
/// make observations about the results of dataflow static analyses.
///
/// (If there are any calls to `rustc_peek` that do not match the
/// expression form above, then that emits an error as well, but those
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, O>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    _attributes: &[ast::Attribute],
    results: &DataflowResults<'tcx, O>,
) where
    O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
{
    debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
    // FIXME: this is not DRY. Figure out way to abstract this and
    // `dataflow::build_sets`. (But note it is doing non-standard
    // stuff, so such generalization may not be realistic.)

    for bb in body.basic_blocks().indices() {
        each_block(tcx, body, results, bb);
    }
}

fn each_block<'tcx, O>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    results: &DataflowResults<'tcx, O>,
    bb: mir::BasicBlock,
) where
    O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
{
    let move_data = results.0.operator.move_data();
    let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = body[bb];

    let (args, span) = match is_rustc_peek(tcx, terminator) {
        Some(args_and_span) => args_and_span,
        None => return,
    };
    assert!(args.len() == 1);
    let peek_arg_place = match args[0] {
        mir::Operand::Copy(ref place @ mir::Place {
            base: mir::PlaceBase::Local(_),
            projection: box [],
        }) |
        mir::Operand::Move(ref place @ mir::Place {
            base: mir::PlaceBase::Local(_),
            projection: box [],
        }) => Some(place),
        _ => None,
    };

    let peek_arg_place = match peek_arg_place {
        Some(arg) => arg,
        None => {
            tcx.sess.diagnostic().span_err(
                span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
            return;
        }
    };

    let mut on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
    let mut trans = results.0.sets.trans_for(bb.index()).clone();

    // Emulate effect of all statements in the block up to (but not
    // including) the borrow within `peek_arg_place`. Do *not* include
    // call to `peek_arg_place` itself (since we are peeking the state
    // of the argument at time immediate preceding Call to
    // `rustc_peek`).

    for (j, stmt) in statements.iter().enumerate() {
        debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
        let (place, rvalue) = match stmt.kind {
            mir::StatementKind::Assign(box(ref place, ref rvalue)) => {
                (place, rvalue)
            }
            mir::StatementKind::FakeRead(..) |
            mir::StatementKind::StorageLive(_) |
            mir::StatementKind::StorageDead(_) |
            mir::StatementKind::InlineAsm { .. } |
            mir::StatementKind::Retag { .. } |
            mir::StatementKind::AscribeUserType(..) |
            mir::StatementKind::Nop => continue,
            mir::StatementKind::SetDiscriminant{ .. } =>
                span_bug!(stmt.source_info.span,
                          "sanity_check should run before Deaggregator inserts SetDiscriminant"),
        };

        if place == peek_arg_place {
            if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue {
                // Okay, our search is over.
                match move_data.rev_lookup.find(peeking_at_place.as_ref()) {
                    LookupResult::Exact(peek_mpi) => {
                        let bit_state = on_entry.contains(peek_mpi);
                        debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
                               place, peeking_at_place, bit_state);
                        if !bit_state {
                            tcx.sess.span_err(span, "rustc_peek: bit not set");
                        }
                    }
                    LookupResult::Parent(..) => {
                        tcx.sess.span_err(span, "rustc_peek: argument untracked");
                    }
                }
                return;
            } else {
                // Our search should have been over, but the input
                // does not match expectations of `rustc_peek` for
                // this sanity_check.
                let msg = "rustc_peek: argument expression \
                           must be immediate borrow of form `&expr`";
                tcx.sess.span_err(span, msg);
            }
        }

        let lhs_mpi = move_data.rev_lookup.find(place.as_ref());

        debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
               place, lhs_mpi, stmt);
        // reset GEN and KILL sets before emulating their effect.
        trans.clear();
        results.0.operator.before_statement_effect(
            &mut trans,
            Location { block: bb, statement_index: j });
        results.0.operator.statement_effect(
            &mut trans,
            Location { block: bb, statement_index: j });
        trans.apply(&mut on_entry);
    }

    results.0.operator.before_terminator_effect(
        &mut trans,
        Location { block: bb, statement_index: statements.len() });

    tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
                                      anticipated pattern; note that \
                                      rustc_peek expects input of \
                                      form `&expr`"));
}

fn is_rustc_peek<'a, 'tcx>(
    tcx: TyCtxt<'tcx>,
    terminator: &'a Option<mir::Terminator<'tcx>>,
) -> Option<(&'a [mir::Operand<'tcx>], Span)> {
    if let Some(mir::Terminator { ref kind, source_info, .. }) = *terminator {
        if let mir::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind {
            if let mir::Operand::Constant(ref func) = *oper {
                if let ty::FnDef(def_id, _) = func.literal.ty.kind {
                    let abi = tcx.fn_sig(def_id).abi();
                    let name = tcx.item_name(def_id);
                    if abi == Abi::RustIntrinsic && name == sym::rustc_peek {
                        return Some((args, source_info.span));
                    }
                }
            }
        }
    }
    return None;
}
<commit_msg>Refactor `rustc_peek`<commit_after>use rustc_target::spec::abi::{Abi};
use syntax::ast;
use syntax::symbol::sym;
use syntax_pos::Span;

use rustc::ty::{self, TyCtxt, Ty};
use rustc::hir::def_id::DefId;
use rustc::mir::{self, Body, Location, Local};
use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};

use crate::dataflow::{do_dataflow, DebugFormatted};
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::BitDenotation;
use crate::dataflow::DataflowResults;
use crate::dataflow::DataflowResultsCursor;
use crate::dataflow::{
    DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
};
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
use crate::dataflow::move_paths::{HasMoveData, MoveData};

use crate::dataflow::has_rustc_mir_with;

pub struct SanityCheck;

impl<'tcx> MirPass<'tcx> for SanityCheck {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = src.def_id();
        if !tcx.has_attr(def_id, sym::rustc_mir) {
            debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
            return;
        } else {
            debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
        }

        let attributes = tcx.get_attrs(def_id);
        let param_env = tcx.param_env(def_id);
        let move_data = MoveData::gather_moves(body, tcx).unwrap();
        let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
        let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
        let flow_inits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        MaybeInitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
        let flow_uninits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        MaybeUninitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
        let flow_def_inits =
            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
                        DefinitelyInitializedPlaces::new(tcx, body, &mdpe),
                        |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));

        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
        }
        if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
            tcx.sess.fatal("stop_after_dataflow ended compilation");
        }
    }
}

/// This function scans `mir` for all calls to the intrinsic
/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
///
/// For each such call, determines what the dataflow bit-state is for
/// the L-value corresponding to `expr`; if the bit-state is a 1, then
/// that call to `rustc_peek` is ignored by the sanity check. If the
/// bit-state is a 0, then this pass emits a error message saying
/// "rustc_peek: bit not set".
///
/// The intention is that one can write unit tests for dataflow by
/// putting code into a compile-fail test and using `rustc_peek` to
/// make observations about the results of dataflow static analyses.
///
/// (If there are any calls to `rustc_peek` that do not match the
/// expression form above, then that emits an error as well, but those
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, O>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    _attributes: &[ast::Attribute],
    results: &DataflowResults<'tcx, O>,
) where O: RustcPeekAt<'tcx> {
    debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);

    let mut cursor = DataflowResultsCursor::new(results, body);

    let peek_calls = body
            .basic_blocks()
            .iter_enumerated()
            .filter_map(|(bb, block_data)| {
                PeekCall::from_terminator(tcx, block_data.terminator())
                    .map(|call| (bb, block_data, call))
            });

    for (bb, block_data, call) in peek_calls {
        // Look for a sequence like the following to indicate that we should be peeking at `_1`:
        //    _2 = &_1;
        //    rustc_peek(_2);
        //
        //    /* or */
        //
        //    _2 = _1;
        //    rustc_peek(_2);
        let (statement_index, peek_rval) = block_data
            .statements
            .iter()
            .enumerate()
            .filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
            .next()
            .expect("call to rustc_peek should be preceded by \
                    assignment to temporary holding its argument");

        match (call.kind, peek_rval) {
            | (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place)))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place)))
            => {
                let loc = Location { block: bb, statement_index };
                cursor.seek(loc);
                let state = cursor.get();
                results.operator().peek_at(tcx, place, state, call);
            }

            _ => {
                let msg = "rustc_peek: argument expression \
                           must be either `place` or `&place`";
                tcx.sess.span_err(call.span, msg);
            }
        }
    }
}

/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
/// RHS of the assignment.
fn value_assigned_to_local<'a, 'tcx>(
    stmt: &'a mir::Statement<'tcx>,
    local: Local,
) -> Option<&'a mir::Rvalue<'tcx>> {
    if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
        if let mir::Place { base: mir::PlaceBase::Local(l), projection: box [] } = place {
            if local == *l {
                return Some(&*rvalue);
            }
        }
    }

    None
}

#[derive(Clone, Copy, Debug)]
enum PeekCallKind {
    ByVal,
    ByRef,
}

impl PeekCallKind {
    fn from_arg_ty(arg: Ty<'_>) -> Self {
        match arg.kind {
            ty::Ref(_, _, _) => PeekCallKind::ByRef,
            _ => PeekCallKind::ByVal,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub struct PeekCall {
    arg: Local,
    kind: PeekCallKind,
    span: Span,
}

impl PeekCall {
    fn from_terminator<'tcx>(
        tcx: TyCtxt<'tcx>,
        terminator: &mir::Terminator<'tcx>,
    ) -> Option<Self> {
        use mir::{Operand, Place, PlaceBase};

        let span = terminator.source_info.span;
        if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
            &terminator.kind
        {
            if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
                let sig = tcx.fn_sig(def_id);
                let name = tcx.item_name(def_id);
                if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
                    return None;
                }

                assert_eq!(args.len(), 1);
                let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
                let arg = match args[0] {
                    | Operand::Copy(Place { base: PlaceBase::Local(local), projection: box [] })
                    | Operand::Move(Place { base: PlaceBase::Local(local), projection: box [] })
                    => local,

                    _ => {
                        tcx.sess.diagnostic().span_err(
                            span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
                        return None;
                    }
                };

                return Some(PeekCall {
                    arg,
                    kind,
                    span,
                });
            }
        }

        None
    }
}

pub trait RustcPeekAt<'tcx>: BitDenotation<'tcx> {
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: &mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    );
}

impl<'tcx, O> RustcPeekAt<'tcx> for O
    where O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
{
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: &mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    ) {
        match self.move_data().rev_lookup.find(place.as_ref()) {
            LookupResult::Exact(peek_mpi) => {
                let bit_state = flow_state.contains(peek_mpi);
                debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
                       call.arg, place, bit_state);
                if !bit_state {
                    tcx.sess.span_err(call.span, "rustc_peek: bit not set");
                }
            }
            LookupResult::Parent(..) => {
                tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
            }
        }
    }
}
<|endoftext|>


<commit_before>use log::{debug};
use goblin::{Object};
use failure::{Error};
use byteorder::{ByteOrder, LittleEndian};

use super::super::super::arch::{RVA};
use super::super::super::workspace::Workspace;
use super::super::{Analyzer};

pub struct ImportsAnalyzer {}

impl ImportsAnalyzer {
    pub fn new() -> ImportsAnalyzer {
        ImportsAnalyzer {}
    }
}

pub struct ImageImportDescriptor {
    pub original_first_thunk: RVA,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name: RVA,
    pub first_thunk: RVA,
}

impl ImageImportDescriptor {
    pub fn is_empty(&self) -> bool {
        self.original_first_thunk == RVA(0x0) &&
            self.time_date_stamp == 0x0 &&
            self.forwarder_chain == 0x0 &&
            self.name == RVA(0x0) &&
            self.first_thunk == RVA(0x0)
    }
}

impl std::fmt::Debug for ImageImportDescriptor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_DESCRIPTOR(FT: {} OFT: {} name: {})",
               self.first_thunk,
               self.original_first_thunk,
               self.name
        )
    }
}

pub fn read_image_import_descriptor(ws: &Workspace, rva: RVA) -> Result<ImageImportDescriptor, Error> {
    let buf = ws.read_bytes(rva, 5 * 4)?;

    // these fields are all u32, even on 64-bit
    let entries: Vec<u32> = buf
        .chunks_exact(0x4)
        .map(|b| LittleEndian::read_u32(b))
        .collect();

    Ok(ImageImportDescriptor {
        original_first_thunk: RVA::from(entries[0] as i64),
        time_date_stamp: entries[1],
        forwarder_chain: entries[2],
        name: RVA::from(entries[3] as i64),
        first_thunk: RVA::from(entries[4] as i64),
    })
}

pub enum ImageThunkData {
    Function(RVA),
    Ordinal(u32),
}

pub fn read_image_thunk_data(ws: &Workspace, rva: RVA) -> Result<ImageThunkData, Error> {
    // see: https://reverseengineering.stackexchange.com/a/13387/17194
    let thunk = ws.read_rva(rva)?;
    let v: i64 = thunk.into();
    if v & (1 << (ws.loader.get_arch().get_pointer_size() * 8 - 1)) > 0x0 {
        // MSB is set, this is an ordinal
        Ok(ImageThunkData::Ordinal((v & 0xFFFF) as u32))
    } else {
        Ok(ImageThunkData::Function(thunk))
    }
}

pub struct ImageImportByName {
    pub hint: u16,
    pub name: String,
}

impl std::fmt::Debug for ImageImportByName {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_BY_NAME(hint: {} name: {})",
               self.hint,
               self.name
        )
    }
}

pub fn read_image_import_by_name(ws: &Workspace, rva: RVA) -> Result<ImageImportByName, Error> {
    Ok(ImageImportByName {
        hint: ws.read_u16(rva)?,
        name: ws.read_utf8(rva + RVA::from(2))?,
    })
}

impl Analyzer for ImportsAnalyzer {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::new();
    /// anal.analyze(&mut ws).unwrap();
    /// assert_eq!(ws.get_symbol(RVA(0x77448)).unwrap(), "api-ms-win-core-appcompat-l1-1-1.dll!BaseReadAppCompatDataForProcess");
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let import_directory = {
            let pe = match Object::parse(&ws.buf) {
                Ok(Object::PE(pe)) => pe,
                _ => panic!("can't analyze unexpected format"),
            };

            let opt_header = match pe.header.optional_header {
                Some(opt_header) => opt_header,
                _ => return Ok(()),
            };

            let import_directory = match opt_header.data_directories.get_import_table() {
                Some(import_directory) => import_directory,
                _ => return Ok(()),
            };

            RVA::from(import_directory.virtual_address as i64)
        };

        debug!("found import directory: {}", import_directory);

        let mut symbols: Vec<(RVA, String)> = vec![];

        let psize: usize = ws.loader.get_arch().get_pointer_size() as usize;
        for i in 0..std::usize::MAX {
            //
            //  0x0                    0x14
            //  +-------------------------+           0x0          ptrsize
            //  | IMAGE_IMPORT_DESCRIPTOR | --------> +------------------+
            //  +-------------------------+ \         | IMAGE_THUNK_DATA | -------> +----------------------+
            //  | ...                     |  |        +------------------+          | IMAGE_IMPORT_BY_NAME |
            //  +-------------------------+  |        | ...              |          +----------------------+
            //  | 00 00 00 00 00 00 000   |  |        +------------------+          | hint u16             |
            //  +-------------------------+  |        | 00 00 00 00 00   |          | name ascii           |
            //                               |        +------------------+          +----------------------+
            //                               |
            //                               +> dll-name (ascii)
            let import_descriptor_rva = import_directory + RVA::from(i * 0x14);
            let import_descriptor = read_image_import_descriptor(ws, import_descriptor_rva)?;
            if import_descriptor.is_empty() {
                break;
            }

            let dll_name = ws.read_utf8(import_descriptor.name)?;
            debug!("found {:?} -> {}", import_descriptor, dll_name);

            for j in 0..std::usize::MAX {
                // the Original First Thunk (OFT) remains constant, and points to the IMAGE_IMPORT_BY_NAME.
                // FT and OFT are parallel arrays.
                let _original_first_thunk = import_descriptor.original_first_thunk + RVA::from(j * psize);

                // the First Thunk (FT) is the pointer that will be overwritten upon load.
                // entries here may not point to the IMAGE_IMPORT_BY_NAME.
                //
                // in practice, using this array works better, as some OFT entries may be empty.
                // see: be24e9d47cfe588a8ced0ac3e453d390 hotfix2.exe
                let first_thunk = import_descriptor.first_thunk + RVA::from(j * psize);

                match read_image_thunk_data(ws, first_thunk)? {
                    ImageThunkData::Function(rva) => {
                        if rva == RVA(0x0) {
                            break;
                        } else {
                            let imp = read_image_import_by_name(ws, rva)?;
                            debug!("found {:?}", imp);

                            symbols.push((first_thunk, format!("{}!{}", dll_name, imp.name)))
                        }
                    },
                    ImageThunkData::Ordinal(ord) => {
                        symbols.push((first_thunk, format!("{}!#{}", dll_name, ord)))
                    },
                };
            }
        }

        for (rva, name) in symbols.iter() {
            debug!("found import: {} -> {}", rva, name);
            ws.make_symbol(*rva, name)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<commit_msg>analysis: imports: read best option from FT or OFT<commit_after>use log::{debug};
use goblin::{Object};
use failure::{Error};
use byteorder::{ByteOrder, LittleEndian};

use super::super::super::arch::{RVA};
use super::super::super::loader::{Permissions};
use super::super::super::workspace::Workspace;
use super::super::{Analyzer};

pub struct ImportsAnalyzer {}

impl ImportsAnalyzer {
    pub fn new() -> ImportsAnalyzer {
        ImportsAnalyzer {}
    }
}

pub struct ImageImportDescriptor {
    pub original_first_thunk: RVA,
    pub time_date_stamp: u32,
    pub forwarder_chain: u32,
    pub name: RVA,
    pub first_thunk: RVA,
}

impl ImageImportDescriptor {
    pub fn is_empty(&self) -> bool {
        self.original_first_thunk == RVA(0x0) &&
            self.time_date_stamp == 0x0 &&
            self.forwarder_chain == 0x0 &&
            self.name == RVA(0x0) &&
            self.first_thunk == RVA(0x0)
    }
}

impl std::fmt::Debug for ImageImportDescriptor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_DESCRIPTOR(FT: {} OFT: {} name: {})",
               self.first_thunk,
               self.original_first_thunk,
               self.name
        )
    }
}

pub fn read_image_import_descriptor(ws: &Workspace, rva: RVA) -> Result<ImageImportDescriptor, Error> {
    let buf = ws.read_bytes(rva, 5 * 4)?;

    // these fields are all u32, even on 64-bit
    let entries: Vec<u32> = buf
        .chunks_exact(0x4)
        .map(|b| LittleEndian::read_u32(b))
        .collect();

    Ok(ImageImportDescriptor {
        original_first_thunk: RVA::from(entries[0] as i64),
        time_date_stamp: entries[1],
        forwarder_chain: entries[2],
        name: RVA::from(entries[3] as i64),
        first_thunk: RVA::from(entries[4] as i64),
    })
}

pub enum ImageThunkData {
    Function(RVA),
    Ordinal(u32),
}

pub fn read_image_thunk_data(ws: &Workspace, rva: RVA) -> Result<ImageThunkData, Error> {
    // see: https://reverseengineering.stackexchange.com/a/13387/17194
    let thunk = ws.read_rva(rva)?;
    let v: i64 = thunk.into();
    if v & (1 << (ws.loader.get_arch().get_pointer_size() * 8 - 1)) > 0x0 {
        // MSB is set, this is an ordinal
        Ok(ImageThunkData::Ordinal((v & 0xFFFF) as u32))
    } else {
        Ok(ImageThunkData::Function(thunk))
    }
}

pub struct ImageImportByName {
    pub hint: u16,
    pub name: String,
}

impl std::fmt::Debug for ImageImportByName {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IMAGE_IMPORT_BY_NAME(hint: {} name: {})",
               self.hint,
               self.name
        )
    }
}

pub fn read_image_import_by_name(ws: &Workspace, rva: RVA) -> Result<ImageImportByName, Error> {
    Ok(ImageImportByName {
        hint: ws.read_u16(rva)?,
        name: ws.read_utf8(rva + RVA::from(2))?,
    })
}

pub fn read_best_thunk_data(ws: &Workspace, original_first_thunk: RVA, first_thunk: RVA) -> Result<ImageThunkData, Error> {
    // the Original First Thunk (OFT) remains constant, and points to the IMAGE_IMPORT_BY_NAME.
    // FT and OFT are parallel arrays.

    // the First Thunk (FT) is the pointer that will be overwritten upon load.
    // entries here may not point to the IMAGE_IMPORT_BY_NAME.
    //
    // in practice, using this array works better, as some OFT entries may be empty.
    // see: be24e9d47cfe588a8ced0ac3e453d390 hotfix2.exe
    //
    // however, this doesn't work if the PE has been dumped from memory
    // (and the FTs fixed up).
    match read_image_thunk_data(ws, first_thunk) {
        Ok(ImageThunkData::Function(rva)) => {
            if rva == RVA(0x0) {
                // end of array, this is a valid entry, return it.
                Ok(ImageThunkData::Function(rva))
            } else if ws.probe(rva, 1, Permissions::R) {
                // seems to be a valid address, return it.
                Ok(ImageThunkData::Function(rva))
            } else {
                // invalid address, so let's try from the OFT array
                // this might happen if we're dealing with a PE that was dumped from memory.
                // the FT might have been overwritten, so we need to use the OFT, instead.
                read_image_thunk_data(ws, original_first_thunk)
            }
        },
        Ok(ImageThunkData::Ordinal(ord)) => {
            // ordinal is a valid entry, return it.
            Ok(ImageThunkData::Ordinal(ord))
        }
        Err(_) => {
            // fall back to the OFT array
            read_image_thunk_data(ws, original_first_thunk)
        }
    }
}

impl Analyzer for ImportsAnalyzer {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::new();
    /// anal.analyze(&mut ws).unwrap();
    /// assert_eq!(ws.get_symbol(RVA(0x77448)).unwrap(), "api-ms-win-core-appcompat-l1-1-1.dll!BaseReadAppCompatDataForProcess");
    /// ```
    fn analyze(&self, ws: &mut Workspace) -> Result<(), Error> {
        let import_directory = {
            let pe = match Object::parse(&ws.buf) {
                Ok(Object::PE(pe)) => pe,
                _ => panic!("can't analyze unexpected format"),
            };

            let opt_header = match pe.header.optional_header {
                Some(opt_header) => opt_header,
                _ => return Ok(()),
            };

            let import_directory = match opt_header.data_directories.get_import_table() {
                Some(import_directory) => import_directory,
                _ => return Ok(()),
            };

            RVA::from(import_directory.virtual_address as i64)
        };

        debug!("found import directory: {}", import_directory);

        let mut symbols: Vec<(RVA, String)> = vec![];

        let psize: usize = ws.loader.get_arch().get_pointer_size() as usize;
        for i in 0..std::usize::MAX {
            //
            //  0x0                    0x14
            //  +-------------------------+           0x0          ptrsize
            //  | IMAGE_IMPORT_DESCRIPTOR | --------> +------------------+
            //  +-------------------------+ \         | IMAGE_THUNK_DATA | -------> +----------------------+
            //  | ...                     |  |        +------------------+          | IMAGE_IMPORT_BY_NAME |
            //  +-------------------------+  |        | ...              |          +----------------------+
            //  | 00 00 00 00 00 00 000   |  |        +------------------+          | hint u16             |
            //  +-------------------------+  |        | 00 00 00 00 00   |          | name ascii           |
            //                               |        +------------------+          +----------------------+
            //                               |
            //                               +> dll-name (ascii)
            let import_descriptor_rva = import_directory + RVA::from(i * 0x14);
            let import_descriptor = read_image_import_descriptor(ws, import_descriptor_rva)?;
            if import_descriptor.is_empty() {
                break;
            }

            let dll_name = ws.read_utf8(import_descriptor.name)?;
            debug!("found {:?} -> {}", import_descriptor, dll_name);

            for j in 0..std::usize::MAX {
                // the Original First Thunk (OFT) remains constant, and points to the IMAGE_IMPORT_BY_NAME.
                // FT and OFT are parallel arrays.
                let _original_first_thunk = import_descriptor.original_first_thunk + RVA::from(j * psize);

                // the First Thunk (FT) is the pointer that will be overwritten upon load.
                // entries here may not point to the IMAGE_IMPORT_BY_NAME.
                let first_thunk = import_descriptor.first_thunk + RVA::from(j * psize);

                match read_best_thunk_data(ws, _original_first_thunk, first_thunk)? {
                    ImageThunkData::Function(rva) => {
                        if rva == RVA(0x0) {
                            // end of array
                            break;
                        } else {
                            let imp = read_image_import_by_name(ws, rva)?;
                            debug!("found {:?}", imp);
                            symbols.push((first_thunk, format!("{}!{}", dll_name, imp.name)))
                        }
                    },
                    ImageThunkData::Ordinal(ord) => {
                        symbols.push((first_thunk, format!("{}!#{}", dll_name, ord)))
                    },
                };
            }
        }

        for (rva, name) in symbols.iter() {
            debug!("found import: {} -> {}", rva, name);
            ws.make_symbol(*rva, name)?;
            ws.analyze()?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use dicom_core::value::C;
use snafu::Snafu;

pub mod rle_lossless;

/// Error conditions when decoding pixel data.
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum DecodeError {
    /// A custom error occurred when decoding,
    /// reported as a dynamic error value
    #[snafu(display("Error decoding pixel data"))]
    Custom {
        source: Box<dyn std::error::Error + Send + 'static>,
    },

    /// A custom error occurred when decoding,
    /// reported as a static message
    #[snafu(display("Error decoding pixel data: {}", message))]
    CustomMessage {
        message: &'static str,
    },

    /// Input pixel data is not encapsulated
    NotEncapsulated,

    /// A required attribute is missing from the DICOM
    #[snafu(display("Missing required attribute: {}", name))]
    MissingAttribute { name: &'static str },
}

/// Error conditions when encoding pixel data.
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum EncodeError {
    /// A custom error when encoding fails
    #[snafu(display("Error encoding pixel data {}", message))]
    CustomEncodeError {
        message: &'static str,
    },

    /// Input pixel data is not native
    NotNative,

    /// Encoding is not implemented
    NotImplemented,
}

pub type DecodeResult<T, E = DecodeError> = Result<T, E>;

pub type EncodeResult<T, E = EncodeError> = Result<T, E>;

#[derive(Debug)]
pub struct RawPixelData {
    /// Either a byte slice/vector if native pixel data
    /// or byte fragments if encapsulated
    pub fragments: C<Vec<u8>>,

    /// The offset table for the fragments,
    /// or empty if there is none
    pub offset_table: C<u32>,
}

/// A DICOM object trait to be interpreted as pixel data.
///
/// This trait extends the concept of DICOM object
/// as defined in [`dicom_object`],
/// in order to retrieve important pieces of the object
/// for pixel data decoding into images or multi-dimensional arrays.
///
/// It is defined in this crate so that
/// transfer syntax implementers only have to depend on `dicom_encoding`.
///
/// [`dicom_object`]: https://docs.rs/dicom_object
pub trait PixelDataObject {
    /// Return the Rows attribute or None if it is not found
    fn rows(&self) -> Option<u16>;

    /// Return the Columns attribute or None if it is not found
    fn cols(&self) -> Option<u16>;

    /// Return the SamplesPerPixel attribute or None if it is not found
    fn samples_per_pixel(&self) -> Option<u16>;

    /// Return the BitsAllocated attribute or None if it is not set
    fn bits_allocated(&self) -> Option<u16>;

    /// Return the NumberOfFrames attribute or None if it is not set
    fn number_of_frames(&self) -> Option<u16>;

    /// Returns the number of fragments or None for native pixel data
    fn number_of_fragments(&self) -> Option<u32>;

    /// Return a specific encoded pixel fragment by index as Vec<u8>
    /// or None if no pixel data is found
    fn fragment(&self, fragment: usize) -> Option<Vec<u8>>;

    /// Should return either a byte slice/vector if native pixel data
    /// or byte fragments if encapsulated.
    /// Returns None if no pixel data is found
    fn raw_pixel_data(&self) -> Option<RawPixelData>;
}

/// Custom options when encoding pixel data into an encapsulated form.
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct EncodeOptions {
    /// The quality of the output image as a number between 0 and 100,
    /// where 100 is the best quality that the encapsulated form can achieve
    /// and smaller values represent smaller data size
    /// with an increasingly higher error.
    /// It is ignored if the transfer syntax only supports lossless compression.
    /// If it does support however,
    /// it is expected that a quality of 100 results in a lossless encoding.
    ///
    /// If this option is not specified,
    /// the output quality is decided automatically by the underlying adapter.
    pub quality: Option<u8>,
}

impl EncodeOptions {
    pub fn new() -> Self {
        Self::default()
    }
}

/// Trait object responsible for decoding and encoding
/// pixel data based on the Transfersyntax.
///
/// Every transfer syntax with encapsulated pixel data
/// should implement these methods.
///
pub trait PixelRWAdapter {
    /// Decode the given DICOM object
    /// containing encapsulated pixel data
    /// into native pixel data as a byte stream.
    ///
    /// It is a necessary precondition that the object's pixel data
    /// is encoded in accordance to the transfer syntax(es)
    /// supported by this adapter.
    /// A `NotEncapsulated` error is returned otherwise.
    ///
    /// The output is a sequence of native pixel values
    /// which follow the image properties of the given object.
    ///
    fn decode(&self, src: &dyn PixelDataObject, dst: &mut Vec<u8>) -> DecodeResult<()>;

    /// Encode a DICOM object's image into the format supported by this adapter,
    /// writing a byte stream of pixel data fragment values
    /// into the given destination.
    ///
    /// It is a necessary precondition that the object's pixel data
    /// is in a _native encoding_.
    /// A `NotNative` error is returned otherwise.
    ///
    /// It is possible that
    /// image encoding is not actually supported by this adapter,
    /// in which case a `NotImplemented` error is returned.
    /// Implementers leave the default method implementation
    /// for this behavior.
    #[allow(unused_variables)]
    fn encode(&self, src: &dyn PixelDataObject, options: EncodeOptions, dst: &mut Vec<u8>) -> EncodeResult<()> {
        Err(EncodeError::NotImplemented)
    }
}

/// Alias type for a dynamically dispatched data adapter.
pub type DynPixelRWAdapter = Box<dyn PixelRWAdapter + Send + Sync>;

/// An immaterial type representing an adapter which is never required.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NeverPixelAdapter {}

impl PixelRWAdapter for NeverPixelAdapter {
    fn decode(&self, _src: &dyn PixelDataObject, _dst: &mut Vec<u8>) -> DecodeResult<()> {
        unreachable!();
    }

    fn encode(&self, _src: &dyn PixelDataObject, _options: EncodeOptions, _dst: &mut Vec<u8>) -> EncodeResult<()> {
        unreachable!();
    }
}
<commit_msg>Add `effort` to pixel data encoding options<commit_after>use dicom_core::value::C;
use snafu::Snafu;

pub mod rle_lossless;

/// Error conditions when decoding pixel data.
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum DecodeError {
    /// A custom error occurred when decoding,
    /// reported as a dynamic error value
    #[snafu(display("Error decoding pixel data"))]
    Custom {
        source: Box<dyn std::error::Error + Send + 'static>,
    },

    /// A custom error occurred when decoding,
    /// reported as a static message
    #[snafu(display("Error decoding pixel data: {}", message))]
    CustomMessage {
        message: &'static str,
    },

    /// Input pixel data is not encapsulated
    NotEncapsulated,

    /// A required attribute is missing from the DICOM
    #[snafu(display("Missing required attribute: {}", name))]
    MissingAttribute { name: &'static str },
}

/// Error conditions when encoding pixel data.
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum EncodeError {
    /// A custom error when encoding fails
    #[snafu(display("Error encoding pixel data {}", message))]
    CustomEncodeError {
        message: &'static str,
    },

    /// Input pixel data is not native
    NotNative,

    /// Encoding is not implemented
    NotImplemented,
}

pub type DecodeResult<T, E = DecodeError> = Result<T, E>;

pub type EncodeResult<T, E = EncodeError> = Result<T, E>;

#[derive(Debug)]
pub struct RawPixelData {
    /// Either a byte slice/vector if native pixel data
    /// or byte fragments if encapsulated
    pub fragments: C<Vec<u8>>,

    /// The offset table for the fragments,
    /// or empty if there is none
    pub offset_table: C<u32>,
}

/// A DICOM object trait to be interpreted as pixel data.
///
/// This trait extends the concept of DICOM object
/// as defined in [`dicom_object`],
/// in order to retrieve important pieces of the object
/// for pixel data decoding into images or multi-dimensional arrays.
///
/// It is defined in this crate so that
/// transfer syntax implementers only have to depend on `dicom_encoding`.
///
/// [`dicom_object`]: https://docs.rs/dicom_object
pub trait PixelDataObject {
    /// Return the Rows attribute or None if it is not found
    fn rows(&self) -> Option<u16>;

    /// Return the Columns attribute or None if it is not found
    fn cols(&self) -> Option<u16>;

    /// Return the SamplesPerPixel attribute or None if it is not found
    fn samples_per_pixel(&self) -> Option<u16>;

    /// Return the BitsAllocated attribute or None if it is not set
    fn bits_allocated(&self) -> Option<u16>;

    /// Return the NumberOfFrames attribute or None if it is not set
    fn number_of_frames(&self) -> Option<u16>;

    /// Returns the number of fragments or None for native pixel data
    fn number_of_fragments(&self) -> Option<u32>;

    /// Return a specific encoded pixel fragment by index as Vec<u8>
    /// or None if no pixel data is found
    fn fragment(&self, fragment: usize) -> Option<Vec<u8>>;

    /// Should return either a byte slice/vector if native pixel data
    /// or byte fragments if encapsulated.
    /// Returns None if no pixel data is found
    fn raw_pixel_data(&self) -> Option<RawPixelData>;
}

/// Custom options when encoding pixel data into an encapsulated form.
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct EncodeOptions {
    /// The quality of the output image as a number between 0 and 100,
    /// where 100 is the best quality that the encapsulated form can achieve
    /// and smaller values represent smaller data size
    /// with an increasingly higher error.
    /// It is ignored if the transfer syntax only supports lossless compression.
    /// If it does support lossless compression,
    /// it is expected that a quality of 100 results in a lossless encoding.
    ///
    /// If this option is not specified,
    /// the output quality is decided automatically by the underlying adapter.
    pub quality: Option<u8>,

    /// The amount of effort that the encoder may take to encode the pixel data,
    /// as a number between 0 and 100.
    /// If supported, higher values result in better compression,
    /// at the expense of more processing time.
    /// Encoders are not required to support this option.
    /// If this option is not specified,
    /// the actual effort is decided by the underlying adapter.
    pub effort: Option<u8>,
}

impl EncodeOptions {
    pub fn new() -> Self {
        Self::default()
    }
}

/// Trait object responsible for decoding and encoding
/// pixel data based on the Transfersyntax.
///
/// Every transfer syntax with encapsulated pixel data
/// should implement these methods.
///
pub trait PixelRWAdapter {
    /// Decode the given DICOM object
    /// containing encapsulated pixel data
    /// into native pixel data as a byte stream.
    ///
    /// It is a necessary precondition that the object's pixel data
    /// is encoded in accordance to the transfer syntax(es)
    /// supported by this adapter.
    /// A `NotEncapsulated` error is returned otherwise.
    ///
    /// The output is a sequence of native pixel values
    /// which follow the image properties of the given object.
    ///
    fn decode(&self, src: &dyn PixelDataObject, dst: &mut Vec<u8>) -> DecodeResult<()>;

    /// Encode a DICOM object's image into the format supported by this adapter,
    /// writing a byte stream of pixel data fragment values
    /// into the given destination.
    ///
    /// It is a necessary precondition that the object's pixel data
    /// is in a _native encoding_.
    /// A `NotNative` error is returned otherwise.
    ///
    /// It is possible that
    /// image encoding is not actually supported by this adapter,
    /// in which case a `NotImplemented` error is returned.
    /// Implementers leave the default method implementation
    /// for this behavior.
    #[allow(unused_variables)]
    fn encode(&self, src: &dyn PixelDataObject, options: EncodeOptions, dst: &mut Vec<u8>) -> EncodeResult<()> {
        Err(EncodeError::NotImplemented)
    }
}

/// Alias type for a dynamically dispatched data adapter.
pub type DynPixelRWAdapter = Box<dyn PixelRWAdapter + Send + Sync>;

/// An immaterial type representing an adapter which is never required.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum NeverPixelAdapter {}

impl PixelRWAdapter for NeverPixelAdapter {
    fn decode(&self, _src: &dyn PixelDataObject, _dst: &mut Vec<u8>) -> DecodeResult<()> {
        unreachable!();
    }

    fn encode(&self, _src: &dyn PixelDataObject, _options: EncodeOptions, _dst: &mut Vec<u8>) -> EncodeResult<()> {
        unreachable!();
    }
}
<|endoftext|>


<commit_before>pub mod types;
<commit_msg>Fix build<commit_after>use self::Type;

pub mod kernel;
<|endoftext|>


<commit_before>// Traits can have associated types:
#![feature(collections)]

use std::fmt::Debug;
//use std::string;

// I thought that associated types could be used to encode existential
// types, as attempted below.  The trait T has associated type t.  I
// define two implementations for this trait, with different
// definitions of t.  Then, I try to create a packed up pair where the
// first and second components of the pair look "the same" (both just
// mention trait T, and each leaves t abstract, via an (implicit)
// existential quantifier.

// The only way I can get rustc to accept this code is when I leave
// these associated types "translucent", and expose their identities
// in terms of concrete types (i.e., String and u32).  This is exactly
// what I want to avoid.  In particular, the tuple pattern that works
// below doesn't generalize to the case where I have an array or a
// list.

// This post may provide a path forward:
// http://stackoverflow.com/questions/28003773/implementing-iterator-using-an-underlying-iterator
// No. It doesn't. It uses the same pattern that I'm using below,
// exposing the identity of the associated type.

trait T : Debug {
    type t;
    fn get (self:&Self) -> Self::t ;
    fn foo (self:&Self, Self::t ) -> String ;
}

#[derive(Debug)]
pub enum Foo { Foo }

#[derive(Debug)]
pub enum Bar { Bar }

impl T for Foo {
    type t = String;
    fn get (self:&Self) -> String { String::from_str("foo") }
    fn foo (self:&Self, s:Self::t) -> String { s }
}

impl T for Bar {
    type t = u32;
    fn get (self:&Self) -> u32 { 0 }
    fn foo (self:&Self, _s:Self::t) -> String { String::from_str("bar") }
}

#[derive(Debug)]
struct Packed {
    two : (Box<T<t=String>>,Box<T<t=u32>>)
}

pub fn main () {
    let a = Packed { two : (Box::new(Foo::Foo),
                            Box::new(Bar::Bar)) } ;
     println!("{:?}", a);
}
<commit_msg>updated note<commit_after>// Traits can have associated types:
#![feature(collections)]

use std::fmt::Debug;
//use std::string;

// I thought that associated types could be used to encode existential
// types, as attempted below.  The trait T has associated type t.  I
// define two implementations for this trait, with different
// definitions of t.  Then, I try to create a packed up pair where the
// first and second components of the pair look "the same" (both just
// mention trait T, and each leaves t abstract, via an (implicit)
// existential quantifier.

// The only way I can get rustc to accept this code is when I leave
// these associated types "translucent", and expose their identities
// in terms of concrete types (i.e., String and u32).  This is exactly
// what I want to avoid.  In particular, the tuple pattern that works
// below doesn't generalize to the case where I have an array or a
// list.

trait T : Debug {
    type t;
    fn get (self:&Self) -> Self::t ;
    fn foo (self:&Self, Self::t ) -> String ;
}

#[derive(Debug)]
pub enum Foo { Foo }

#[derive(Debug)]
pub enum Bar { Bar }

impl T for Foo {
    type t = String;
    fn get (self:&Self) -> String { String::from_str("foo") }
    fn foo (self:&Self, s:Self::t) -> String { s }
}

impl T for Bar {
    type t = u32;
    fn get (self:&Self) -> u32 { 0 }
    fn foo (self:&Self, _s:Self::t) -> String { String::from_str("bar") }
}

#[derive(Debug)]
struct Packed {
    two : (Box<T<t=String>>,Box<T<t=u32>>)
}

pub fn main () {
    let a = Packed { two : (Box::new(Foo::Foo),
                            Box::new(Bar::Bar)) } ;
     println!("{:?}", a);
}
<|endoftext|>