diff --git a/Cargo.lock b/Cargo.lock index 2de9c84e3b031..579a2d8b3e0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,6 +1000,13 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "daikon-tests" +version = "0.1.0" +dependencies = [ + "colored 3.0.0", +] + [[package]] name = "darling" version = "0.20.11" diff --git a/Cargo.toml b/Cargo.toml index 67c7a9d67edc8..69f4e017eb957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ # tidy-alphabetical-start - "compiler/rustc", + "compiler/rustc", "daikon-tests", "src/build_helper", "src/rustc-std-workspace/rustc-std-workspace-alloc", "src/rustc-std-workspace/rustc-std-workspace-core", diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index be8e1d22c9dbf..35d602a948a27 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -278,7 +278,7 @@ macro_rules! generate_flat_map_visitor_fns { } } - fn $name( + pub fn $name( vis: &mut V, values: &mut ThinVec<$Ty>, $( diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8dab520cf36b9..2b608c3dba5ad 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -16,6 +16,8 @@ #![feature(try_blocks)] // tidy-alphabetical-end +use rustc_parse::parser::item::{OUTPUT_NAME, jot_output_name}; + use std::cmp::max; use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsString; @@ -280,6 +282,31 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) let sess = &compiler.sess; let codegen_backend = &*compiler.codegen_backend; + // get output name for decls, dtrace, and pp + // try input/output file names first. Prefer output to input. + match &sess.io.output_file { + None => match &sess.io.input { + Input::File(path) => { + jot_output_name(String::from(path.to_str().unwrap())); + } + _ => {} + }, + Some(ofile) => match &ofile { + OutFileName::Real(path) => { + *OUTPUT_NAME.lock().unwrap() = String::from(path.to_str().unwrap()); + } + _ => {} + }, + } + + // this is the expected path for builds with cargo + match &sess.opts.crate_name { + Some(name) => { + *OUTPUT_NAME.lock().unwrap() = String::from(name); + } + _ => {} + } + // This is used for early exits unrelated to errors. E.g. when just // printing some information without compiling, or exiting immediately // after parsing, etc. diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3dfa3cdcc3560..3c72291e04028 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -3,6 +3,23 @@ use std::rc::Rc; use std::sync::Arc; use std::{iter, mem, slice}; +use rustc_ast::visit::FnKind; +#[allow(unused_imports)] +use rustc_ast::{ + AngleBracketedArg, Block, Expr, FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, Item, Pat, + Path, VariantData, +}; +#[allow(unused_imports)] +use rustc_parse::parser::daikon_strs::{ + BOOL, CHAR, F32, F64, I8, I16, I32, I64, I128, ISIZE, STR, STRING, U8, U16, U32, U64, U128, + UNIT, USIZE, VEC, +}; +use rustc_parse::parser::item::{DO_VISITOR, OUTPUT_NAME}; +use std::collections::HashMap; +use std::io::Write; +use std::sync::{LazyLock, Mutex}; +use thin_vec::ThinVec; + use rustc_ast::mut_visit::*; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; @@ -455,6 +472,1454 @@ impl Invocation { } } +// Given a parameter pat, return its identifier name in a String +fn get_param_ident(pat: &Box) -> String { + match &pat.kind { + PatKind::Ident(_mode, ident, None) => String::from(ident.as_str()), + _ => panic!("Formal arg does not have simple identifier"), + } +} + +// Given a Rust type, return its "Java" type if there is a match +fn get_prim_rep_type(ty_str: &str) -> String { + if ty_str == I8 + || ty_str == I16 + || ty_str == I32 + || ty_str == I64 + || ty_str == I128 + || ty_str == ISIZE + || ty_str == U8 + || ty_str == U16 + || ty_str == U32 + || ty_str == U64 + || ty_str == U128 + || ty_str == USIZE + { + return String::from("int"); + } else if ty_str == F32 || ty_str == F64 { + return String::from(""); + } else if ty_str == CHAR { + return String::from("char"); + } else if ty_str == BOOL { + return String::from("boolean"); + } else if ty_str == UNIT { + return String::from(""); + } else if ty_str == STR || ty_str == STRING { + return String::from("java.lang.String"); + } + String::from("") +} + +// Given the arguments to a Vec or array, return a RepType +// enum representing the Vec/array. +fn grok_vec_args(path: &Path) -> RepType { + let mut is_ref = false; + match &path.segments[path.segments.len() - 1].args { + None => panic!("Vec args has no type name"), + Some(args) => match &**args { + GenericArgs::AngleBracketed(brack_args) => match &brack_args.args[0] { + AngleBracketedArg::Arg(arg) => match &arg { + GenericArg::Type(arg_type) => { + match &get_rep_type(&arg_type.kind, &mut is_ref) { + RepType::Prim(arg_p_type) => RepType::PrimArray(arg_p_type.to_string()), + RepType::HashCodeStruct(struct_type) => { + RepType::HashCodeArray(struct_type.to_string()) + } + _ => panic!("Multi-dim vec/array not supported"), + } + } + _ => panic!("Grok args failed 1"), + }, + _ => panic!("Grok args failed 2"), + }, + _ => panic!("Grok args failed 3"), + }, + } +} + +// Capable of representing the rep-type of a Rust type +// String payload represents the corresponding "Java" type +// i32 -> Prim("int") +// &[i32] -> PrimArray("int") +// [X; 2] -> HashCodeArray("X") +// &'a X -> HashCodeStruct("X") +#[derive(PartialEq)] +enum RepType { + Prim(String), + PrimArray(String), + HashCodeArray(String), + HashCodeStruct(String), +} + +// Given a Rust type kind, return its RepType. Also note whether the type +// is a reference with is_ref. +fn get_rep_type(kind: &TyKind, is_ref: &mut bool) -> RepType { + match &kind { + TyKind::Array(arr_type, _) => match &get_rep_type(&arr_type.kind, is_ref) { + RepType::Prim(p_type) => RepType::PrimArray(String::from(p_type)), + RepType::HashCodeStruct(basic_type) => RepType::HashCodeArray(String::from(basic_type)), + _ => panic!("higher-dim arrays not supported"), + }, + TyKind::Slice(arr_type) => match &get_rep_type(&arr_type.kind, is_ref) { + RepType::Prim(p_type) => RepType::PrimArray(String::from(p_type)), + RepType::HashCodeStruct(basic_type) => RepType::HashCodeArray(String::from(basic_type)), + _ => panic!("higher-dim arrays not supported"), + }, + TyKind::Ptr(_) => todo!(), + TyKind::Ref(_, mut_ty) => { + *is_ref = true; + return get_rep_type(&mut_ty.ty.kind, is_ref); + } + TyKind::Path(_, path) => { + if path.segments.len() == 0 { + panic!("Path has no type"); + } + let ty_string = path.segments[path.segments.len() - 1].ident.as_str(); + let maybe_prim_rep = get_prim_rep_type(ty_string); + if maybe_prim_rep != "" { + return RepType::Prim(maybe_prim_rep); + } + if ty_string == VEC { + // TODO + return grok_vec_args(&path); + } + return RepType::HashCodeStruct(String::from(ty_string)); + } + _ => todo!(), + } +} + +// Unused +#[allow(rustc::default_hash_types)] +fn map_params(decl: &Box) -> HashMap { + let mut res = HashMap::new(); + let mut i = 0; + while i < decl.inputs.len() { + res.insert(get_param_ident(&decl.inputs[i].pat), i as i32); + i += 1; + } + res +} + +// This struct is responsible for building a map from identifier to Struct +// This will not be needed once we do a first pass, we can read from a /tmp +// file to fill this purpose. +#[allow(rustc::default_hash_types)] +struct DeclsHashMapBuilder<'a> { + pub map: &'a mut HashMap>, +} + +impl<'a> Visitor<'a> for DeclsHashMapBuilder<'a> { + // Visit structs and fill hash map. + fn visit_item(&mut self, item: &'a Item) { + match &item.kind { + ItemKind::Struct(ident, _, variant_data) => match variant_data { + VariantData::Struct { fields: _, recovered: _ } => { + self.map.insert(String::from(ident.as_str()), Box::new(item.clone())); + } + VariantData::Tuple(_, _) => {} + _ => {} + }, + _ => {} + } + + visit::walk_item(self, item); + } +} + +// Main struct for walking functions to write the decls file. +// map allows for quick retrieval of struct fields when a struct +// parameter is encountered. +// depth_limit tells us when to stop writing decls for recursive structs. +#[allow(rustc::default_hash_types)] +struct DaikonDeclsVisitor<'a> { + pub map: &'a HashMap>, + pub depth_limit: u32, +} + +// Represents a parameter or return value which must be written to decls. +// map: map from String to struct definition with field declarations +// var_name: parameter name, or "return" for return values. +// dec_type: Declared type of the value (dec-type for Daikon) +// rep_type: Rep type of the value (rep-type for Daikon) +// key: If the value is a struct, contains the struct type name for lookup, +// otherwise None. +// field_decls: If the value is a struct, represents decl records for the +// fields of this struct +// contents: If the value is Vec or array, a decls record for the contents +// of this outer container. +// Note: it is maintained that only one of field_decls or contents will be Some. +#[allow(rustc::default_hash_types)] +struct TopLevlDecl<'a> { + pub map: &'a HashMap>, + pub var_name: String, + pub dec_type: String, + pub rep_type: String, + pub key: Option, // struct name for looking up structs if this is a struct + pub field_decls: Option>>, + pub contents: Option>, +} + +// Represents a field decl of a struct at some arb. depth. +// enclosing_var: the identifier of the struct which contains this field. +// field_name: name of this field +// See TopLevlDecl for other fields. +#[allow(rustc::default_hash_types)] +struct FieldDecl<'a> { + pub map: &'a HashMap>, + pub var_name: String, + pub dec_type: String, + pub rep_type: String, + pub enclosing_var: String, + pub field_name: String, + pub key: Option, + pub field_decls: Option>>, + pub contents: Option>, +} + +// Represents the array contents decl record (i.e., arr[..] or arr[..].g rather than arr) +// enclosing_var: name of the outer container for this array or Vec +// sub_contents: If we are an array of structs, we need ArrayContents for each field. +// See TopLevlDecl for other fields. +#[allow(rustc::default_hash_types)] +struct ArrayContents<'a> { + pub map: &'a HashMap>, + pub var_name: String, + pub dec_type: String, + pub rep_type: String, + pub enclosing_var: String, + pub key: Option, + pub sub_contents: Option>>, // only if this is a hashcode[], for printing subfield array records +} + +impl<'a> ArrayContents<'a> { + // Write out an ArrayContents to the decls file. We assume the cursor is at + // the right spot and we simply append ourselves to the file. + fn write(&mut self) { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot open decls"), + Some(decls) => { + if self.var_name == "false" { + return; + } + + writeln!(decls, "variable {}", self.var_name).ok(); + writeln!(decls, " var-kind array").ok(); + writeln!(decls, " enclosing-var {}", self.enclosing_var).ok(); + writeln!(decls, " array 1").ok(); + writeln!(decls, " dec-type {}", self.dec_type).ok(); + writeln!(decls, " rep-type {}", self.rep_type).ok(); + writeln!(decls, " comparability -1").ok(); + } + } + + match &mut self.sub_contents { + None => {} + Some(sub_contents) => { + let mut i = 0; + while i < sub_contents.len() { + sub_contents[i].write(); + i += 1; + } + } + } + } + + // If we are an array of structs, use our key to fetch field definitions + // of our struct type. + fn get_fields(&self, do_write: &mut bool) -> ThinVec { + // use self.key to look up who we are. + match &self.key { + None => panic!("No key for get_fields"), + Some(key) => { + let struct_item = self.map.get(key); + match &struct_item { + None => { + // This is an Enum or Union or ? + *do_write = false; + ThinVec::new() + } + Some(struct_item) => match &struct_item.kind { + ItemKind::Struct(_, _, variant_data) => match variant_data { + VariantData::Struct { fields, recovered: _ } => fields.clone(), + _ => panic!("Struct is not VariantData::Struct"), + }, + _ => panic!("struct_item is not a struct"), + }, + } + } + } + } + + // If we are an array of structs, recursively populate sub_contents by creating + // a new ArrayContents for each field. + // do_write: I think this was a hack for avoiding structs/enums/unions which did + // not belong to the crate. That is again an ongoing issue with the /tmp + // file we need to create in the first pass. + fn build_contents(&mut self, depth_limit: u32, do_write: &mut bool) { + if depth_limit == 0 { + return; + } + + // fields of the struct in this array + let fields = self.get_fields(do_write); + if !*do_write { + return; + } + + let mut i = 0; + while i < fields.len() { + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + let var_name = format!("{}.{}", self.var_name, field_name); + let mut is_ref = false; + let mut do_write = true; + let var_decl = match &get_rep_type(&fields[i].ty.kind, &mut is_ref) { + RepType::Prim(p_type) => ArrayContents { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", p_type), + rep_type: format!("{}[]", p_type), + enclosing_var: self.var_name.clone(), + key: None, + sub_contents: None, + }, + RepType::HashCodeStruct(ty_string) => { + let mut tmp = ArrayContents { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode[]"), + enclosing_var: self.var_name.clone(), + key: Some(ty_string.clone()), + sub_contents: Some(Vec::new()), + }; + tmp.build_contents(depth_limit - 1, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut tmp.sub_contents { + None => panic!("Expected some field_decls 1"), + Some(sub_contents) => { + let mut j = 0; + while j < sub_contents.len() { + sub_contents[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + tmp + } + RepType::PrimArray(_) => { + // only print pointers + ArrayContents { + map: self.map, + var_name: var_name.clone(), + dec_type: String::from(""), + rep_type: String::from("hashcode[]"), + enclosing_var: self.var_name.clone(), + key: None, // we shouldn't be using this in write. + sub_contents: None, + } + } + RepType::HashCodeArray(_) => { + // only print pointers + ArrayContents { + map: self.map, + var_name: var_name.clone(), + dec_type: String::from(""), + rep_type: String::from("hashcode[]"), + enclosing_var: self.var_name.clone(), + key: None, + sub_contents: None, + } + } + }; + match &mut self.sub_contents { + None => panic!("No sub_contents in build_contents"), + Some(sub_contents) => { + sub_contents.push(var_decl); + } + } + + i += 1; + } + } +} + +impl<'a> FieldDecl<'a> { + // Write this entire FieldDecl to the decls file. + fn write(&mut self) { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot open decls"), + Some(decls) => { + if self.var_name == "false" { + return; + } + + writeln!(decls, "variable {}", self.var_name).ok(); + writeln!(decls, " var-kind field {}", self.field_name).ok(); + writeln!(decls, " enclosing-var {}", self.enclosing_var).ok(); + writeln!(decls, " dec-type {}", self.dec_type).ok(); + writeln!(decls, " rep-type {}", self.rep_type).ok(); + writeln!(decls, " comparability -1").ok(); + } + } + + match &mut self.field_decls { + None => {} + Some(field_decls) => { + let mut i = 0; + while i < field_decls.len() { + field_decls[i].write(); + i += 1; + } + return; + } + } + match &mut self.contents { + None => {} + Some(contents) => { + contents.write(); + } + } + } + + // If we are a struct type field, use our key to get field definitions + // for the struct type. + fn get_fields(&self, do_write: &mut bool) -> ThinVec { + // use self.key to look up who we are. + match &self.key { + None => panic!("No key for get_fields"), + Some(key) => { + let struct_item = self.map.get(key); + match &struct_item { + None => { + *do_write = false; + ThinVec::new() + } + Some(struct_item) => match &struct_item.kind { + ItemKind::Struct(_, _, variant_data) => match variant_data { + VariantData::Struct { fields, recovered: _ } => fields.clone(), + _ => panic!("Struct is not VariantData::Struct"), + }, + _ => panic!("struct_item is not a struct"), + }, + } + } + } + } + + // If we are a struct field, recursively build up our field_decls by + // creating a new FieldDecl for each field. + fn build_fields(&mut self, depth_limit: u32, do_write: &mut bool) { + if depth_limit == 0 { + // Invalidate ourselves for writing? Or will writing stop too... + return; + } + + let fields = self.get_fields(do_write); + if !*do_write { + return; + } + // do we really need error checking after this? maybe, cause you can have Vec where Struct has an Enum field, + + let mut i = 0; + while i < fields.len() { + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + let var_name = format!("{}.{}", self.var_name, field_name); + let mut is_ref = false; + let mut do_write = true; + let var_decl = match &get_rep_type(&fields[i].ty.kind, &mut is_ref) { + RepType::Prim(p_type) => { + FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: p_type.clone(), + rep_type: p_type.clone(), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: None, + field_decls: None, + contents: None, + } // Ready to write. + } + RepType::HashCodeStruct(ty_string) => { + let mut tmp = FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: ty_string.clone(), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: Some(ty_string.clone()), + field_decls: Some(Vec::new()), + contents: None, + }; + tmp.build_fields(depth_limit - 1, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut tmp.field_decls { + None => panic!("Expected some field_decls 1"), + Some(field_decls) => { + let mut j = 0; + while j < field_decls.len() { + field_decls[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + tmp + } + RepType::PrimArray(p_type) => { + FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", p_type), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: None, + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", p_type), + rep_type: format!("{}[]", p_type), + enclosing_var: var_name.clone(), + key: None, + sub_contents: None, + }), // Ready to write. + } + } + RepType::HashCodeArray(ty_string) => { + let mut tmp = FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: Some(ty_string.clone()), + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode[]"), + enclosing_var: var_name.clone(), + key: Some(ty_string.clone()), + sub_contents: Some(Vec::new()), + }), + }; + match &mut tmp.contents { + None => panic!(""), + Some(contents) => { + contents.build_contents(depth_limit - 1, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut contents.sub_contents { + None => panic!("Expected some field_decls 1"), + Some(sub_contents) => { + let mut j = 0; + while j < sub_contents.len() { + sub_contents[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + } + } + tmp + } + }; + match &mut self.field_decls { + None => panic!("No field_decls in build_fields"), + Some(field_decls) => { + field_decls.push(var_decl); + } + } + + i += 1; + } + } +} + +impl<'a> TopLevlDecl<'a> { + // Write this entire TopLevlDecl to the decls file. + fn write(&mut self) { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot open decls"), + Some(decls) => { + if self.var_name == "false" { + return; + } + + writeln!(decls, "variable {}", self.var_name).ok(); + writeln!(decls, " var-kind variable").ok(); + writeln!(decls, " dec-type {}", self.dec_type).ok(); + writeln!(decls, " rep-type {}", self.rep_type).ok(); + writeln!(decls, " flags is_param").ok(); + writeln!(decls, " comparability -1").ok(); + } + } + + match &mut self.field_decls { + None => {} + Some(field_decls) => { + let mut i = 0; + while i < field_decls.len() { + field_decls[i].write(); + i += 1; + } + return; + } + } + match &mut self.contents { + None => {} + Some(contents) => { + contents.write(); + } + } + } + + // If we are a struct variable, recursively build declarations for our + // fields. Almost or maybe exactly the same as FieldDecl::build_fields. + fn build_fields(&mut self, depth_limit: u32, do_write: &mut bool) { + if depth_limit == 0 { + // Invalidate ourselves for writing? Or will writing stop too... + return; + } + + let fields = self.get_fields(do_write); + if !*do_write { + return; + } + + let mut i = 0; + while i < fields.len() { + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + let var_name = format!("{}.{}", self.var_name, field_name); + let mut is_ref = false; + let mut do_write = true; + let var_decl = match &get_rep_type(&fields[i].ty.kind, &mut is_ref) { + RepType::Prim(p_type) => { + FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: p_type.clone(), + rep_type: p_type.clone(), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: None, + field_decls: None, + contents: None, + } // Ready to write. + } + RepType::HashCodeStruct(ty_string) => { + let mut tmp = FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: ty_string.clone(), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: Some(ty_string.clone()), + field_decls: Some(Vec::new()), + contents: None, + }; + tmp.build_fields(depth_limit - 1, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut tmp.field_decls { + None => panic!("Expected some field_decls 1"), + Some(field_decls) => { + let mut j = 0; + while j < field_decls.len() { + field_decls[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + tmp + } + RepType::PrimArray(p_type) => { + FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", p_type), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: None, + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", p_type), + rep_type: format!("{}[]", p_type), + enclosing_var: var_name.clone(), + key: None, + sub_contents: None, + }), // Ready to write. + } + } + RepType::HashCodeArray(ty_string) => { + let mut tmp = FieldDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode"), + enclosing_var: self.var_name.clone(), + field_name: field_name.clone(), + key: Some(ty_string.clone()), + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode[]"), + enclosing_var: var_name.clone(), + key: Some(ty_string.clone()), + sub_contents: Some(Vec::new()), + }), + }; + match &mut tmp.contents { + None => panic!(""), + Some(contents) => { + contents.build_contents(depth_limit - 1, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointers is valid. + match &mut contents.sub_contents { + None => panic!("Expected some field_decls 1"), + Some(sub_contents) => { + let mut j = 0; + while j < sub_contents.len() { + sub_contents[j].var_name = String::from("false"); + j += 1; + } + } + } + } + + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + } + } + tmp + } + }; + match &mut self.field_decls { + None => panic!("No field_decls in build_fields"), + Some(field_decls) => { + field_decls.push(var_decl); + } + } + + i += 1; + } + } + + // If we are a struct variable, use our key to get field definitions + // for our struct type. + fn get_fields(&self, do_write: &mut bool) -> ThinVec { + // use self.key to look up who we are. + match &self.key { + None => panic!("No key for get_fields"), + Some(key) => { + let struct_item = self.map.get(key); + match &struct_item { + None => { + *do_write = false; + ThinVec::new() + } + Some(struct_item) => match &struct_item.kind { + ItemKind::Struct(_, _, variant_data) => match variant_data { + VariantData::Struct { fields, recovered: _ } => fields.clone(), + _ => panic!("Struct is not VariantData::Struct"), + }, + _ => panic!("struct_item is not a struct"), + }, + } + } + } + } +} + +// Helper to write function entries into the decls file. +fn write_entry(ppt_name: String) { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot access decls"), + Some(decls) => { + writeln!(decls, "ppt {}:::ENTER", ppt_name).ok(); + writeln!(decls, "ppt-type enter").ok(); + } + } +} + +// Helper to write function exits into the decls file. +fn write_exit(ppt_name: String, exit_counter: usize) { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot access decls"), + Some(decls) => { + writeln!(decls, "ppt {}:::EXIT{}", ppt_name, exit_counter).ok(); + writeln!(decls, "ppt-type exit").ok(); + } + } +} + +// Helper to add a newline in the decls file. +fn write_newline() { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot access decls"), + Some(decls) => { + writeln!(decls, "").ok(); + } + } +} + +// Helper to write metadata header into the decls file. +fn write_header() { + match &mut *DECLS.lock().unwrap() { + None => panic!("Cannot access decls"), + Some(decls) => { + writeln!(decls, "decl-version 2.0").ok(); + writeln!(decls, "input-language Rust").ok(); + writeln!(decls, "var-comparability implicit").ok(); + } + } +} + +impl<'a> DaikonDeclsVisitor<'a> { + // Walk an if expression looking for returns. + // See rustc_parse::parser::item::grok_expr_for_if. + #[allow(rustc::default_hash_types)] + fn grok_expr_for_if( + &mut self, + expr: &Box, + exit_counter: &mut usize, + ppt_name: String, + param_decls: &mut Vec>, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + ) { + match &expr.kind { + ExprKind::Block(block, _) => { + self.grok_block( + ppt_name.clone(), + block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + } + ExprKind::If(_, if_block, None) => { + self.grok_block( + ppt_name.clone(), + if_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + } + ExprKind::If(_, if_block, Some(another_expr)) => { + self.grok_block( + ppt_name.clone(), + if_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + self.grok_expr_for_if( + another_expr, + exit_counter, + ppt_name.clone(), + param_decls, + ¶m_to_block_idx, + &ret_ty, + ); + } + _ => panic!("Internal error handling if stmt with else!"), + } + } + + // Process an entire stmt to identify an exit point or recurse on blocks. + // See rustc_parse::parser::item::grok_stmt. + #[allow(rustc::default_hash_types)] + fn grok_stmt( + &mut self, + loc: usize, + body: &Box, + exit_counter: &mut usize, + ppt_name: String, + param_decls: &mut Vec>, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + ) -> usize { + let mut i = loc; + match &body.stmts[i].kind { + StmtKind::Let(_local) => { + return i + 1; + } + StmtKind::Item(_item) => { + return i + 1; + } + StmtKind::Expr(no_semi_expr) => match &no_semi_expr.kind { + // Blocks. + // recurse on nested block, + // but we still only grokked one (block) stmt, so just + // move to the next stmt (return i+1) + ExprKind::Block(block, _) => { + self.grok_block( + ppt_name.clone(), + block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + return i + 1; + } + ExprKind::If(_, if_block, None) => { + // no else + self.grok_block( + ppt_name.clone(), + if_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + return i + 1; + } + ExprKind::If(_, if_block, Some(expr)) => { + // yes else + self.grok_block( + ppt_name.clone(), + if_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + + self.grok_expr_for_if( + expr, + exit_counter, + ppt_name.clone(), + param_decls, + ¶m_to_block_idx, + &ret_ty, + ); + return i + 1; + } + ExprKind::While(_, while_block, _) => { + self.grok_block( + ppt_name.clone(), + while_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + return i + 1; + } + ExprKind::ForLoop { pat: _, iter: _, body: for_block, label: _, kind: _ } => { + self.grok_block( + ppt_name.clone(), + for_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + return i + 1; + } + ExprKind::Loop(loop_block, _, _) => { + self.grok_block( + ppt_name.clone(), + loop_block, + param_decls, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + ); + return i + 1; + } // missing Match blocks, TryBlock, Const block? probably more + _ => {} + }, + // Look for returns. dtrace passes have run, so all exit points should + // be identifiable by an explicit return stmt. + StmtKind::Semi(semi) => match &semi.kind { + ExprKind::Ret(None) => { + write_exit(ppt_name.clone(), *exit_counter); + *exit_counter += 1; + let mut idx = 0; + while idx < param_decls.len() { + param_decls[idx].write(); + idx += 1; + } + write_newline(); + + // we're sitting on the void return we just processed, so inc + // to move on + i += 1; + } + ExprKind::Ret(Some(_)) => { + write_exit(ppt_name.clone(), *exit_counter); + *exit_counter += 1; + let mut idx = 0; + while idx < param_decls.len() { + param_decls[idx].write(); + idx += 1; + } + + // make return TopLevlDecl + match &ret_ty { + FnRetTy::Default(_) => {} // no return record to be had. + FnRetTy::Ty(ty) => { + let var_name = String::from("return"); + let mut is_ref = false; + let mut do_write = true; + let mut return_decl = match &get_rep_type(&ty.kind, &mut is_ref) { + RepType::Prim(p_type) => { + TopLevlDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: p_type.clone(), + rep_type: p_type.clone(), + key: None, + field_decls: None, + contents: None, + } // Ready to write this var decl. + } + RepType::HashCodeStruct(ty_string) => { + // do_write = !ty_string.starts_with("Option") && !ty_string.starts_with("Result"); + // println!("do_write is {} for {}", do_write, ty_string); + let mut tmp = TopLevlDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: ty_string.clone(), + rep_type: String::from("hashcode"), + key: Some(ty_string.clone()), + field_decls: Some(Vec::new()), + contents: None, + }; + tmp.build_fields(self.depth_limit, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut tmp.field_decls { + None => panic!("Expected some field_decls 1"), + Some(field_decls) => { + let mut j = 0; + while j < field_decls.len() { + field_decls[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") + || ty_string.starts_with("Result") + { + // this record is also invalid + tmp.var_name = String::from("false"); + } + tmp + } + RepType::PrimArray(p_type) => { + TopLevlDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", p_type), + rep_type: String::from("hashcode"), + key: None, + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", p_type), + rep_type: format!("{}[]", p_type), + enclosing_var: var_name.clone(), + key: None, + sub_contents: None, + }), // Ready to write this var_decl. + } + } + RepType::HashCodeArray(ty_string) => { + let mut tmp = TopLevlDecl { + map: self.map, + var_name: var_name.clone(), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode"), + key: Some(ty_string.clone()), + field_decls: None, + contents: Some(ArrayContents { + map: self.map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode[]"), + enclosing_var: var_name.clone(), + key: Some(ty_string.clone()), + sub_contents: Some(Vec::new()), + }), + }; + match &mut tmp.contents { + None => panic!(""), + Some(contents) => { + contents.build_contents( + self.depth_limit - 1, + &mut do_write, + ); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointers is valid. + match &mut contents.sub_contents { + None => panic!("Expected some field_decls 1"), + Some(sub_contents) => { + let mut j = 0; + while j < sub_contents.len() { + sub_contents[j].var_name = + String::from("false"); + j += 1; + } + } + } + } + + if ty_string.starts_with("Option") + || ty_string.starts_with("Result") + { + // this record is also invalid + tmp.var_name = String::from("false"); + } + } + } + tmp + } + }; + return_decl.write(); + } + } + + write_newline(); + // probably: + i += 1; + } + ExprKind::Call(_call, _params) => { + return i + 1; + } // Maybe check for drop and other invalidations + _ => { + return i + 1; + } // other things you overlooked + }, + // StmtKind::Expr(no_semi_expr) => match &no_semi_expr.kind { + // ExprKind::Match(..) => { + // return i + 1; + // } + // _ => panic!("is this non-semi expr a return or a valid non-semi expr?"), + // }, + _ => { + return i + 1; + } + } + i + } + + // Walk a new block looking for exit points and nested blocks. + // See rustc_parse::parser::item::grok_block. + #[allow(rustc::default_hash_types)] + fn grok_block( + &mut self, + ppt_name: String, + body: &Box, + param_decls: &mut Vec>, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + exit_counter: &mut usize, + ) { + let mut i = 0; + + // assuming no unreachable statements. + while i < body.stmts.len() { + // make sure loop bound is growing as we insert stmts + i = self.grok_stmt( + i, + body, + exit_counter, + ppt_name.clone(), + param_decls, + ¶m_to_block_idx, + &ret_ty, + ); // match on Semi and blocks mainly for now, find return ; and add an exit point. + } + } + + // is it a good idea to store which params are valid at each exit + // ppt for the decls pass which happens after this? + // then the decls pass just needs to + // 1: visit_item to build HashMap + // 2: visit_fn, grok sig, and grok exit ppts using structural + // recursion on StructNodes for nesting. Need to use depth counter + // for a base case. + + // Walk a function body looking for exit points. + // See rustc_parse::parser::item::grok_fn_body. + #[allow(rustc::default_hash_types)] + fn grok_fn_body( + &mut self, + ppt_name: String, + body: &Box, + param_decls: &mut Vec>, + param_to_block_idx: HashMap, + ret_ty: &FnRetTy, + ) { + // look for returns and nested blocks (recurse in those cases) + let mut exit_counter = 1; + + // assuming no unreachable statements. + let mut i = 0; + while i < body.stmts.len() { + // make sure loop bound is growing as we insert stmts + i = self.grok_stmt( + i, + body, + &mut exit_counter, + ppt_name.clone(), + param_decls, + ¶m_to_block_idx, + &ret_ty, + ); + } + } +} + +// Process a function signature and build up a new Vec +// ready to be subsequently written to the decls file before we +// walk the function body looking for exit points. +// See rustc_parse::parser::item::grok_fn_sig. +#[allow(rustc::default_hash_types)] +fn grok_fn_sig<'a>( + decl: &'a Box, + map: &'a HashMap>, + depth_limit: u32, +) -> Vec> { + let mut var_decls: Vec> = Vec::new(); + let mut i = 0; + while i < decl.inputs.len() { + let var_name = get_param_ident(&decl.inputs[i].pat); + let mut is_ref = false; + let mut do_write = true; + let toplevl_decl = match &get_rep_type(&decl.inputs[i].ty.kind, &mut is_ref) { + RepType::Prim(p_type) => { + TopLevlDecl { + map, + var_name: var_name.clone(), + dec_type: p_type.clone(), + rep_type: p_type.clone(), + key: None, + field_decls: None, + contents: None, + } // Ready to write this var decl. + } + RepType::HashCodeStruct(ty_string) => { + let mut tmp = TopLevlDecl { + map, + var_name: var_name.clone(), + dec_type: ty_string.clone(), + rep_type: String::from("hashcode"), + key: Some(ty_string.clone()), + field_decls: Some(Vec::new()), + contents: None, + }; + tmp.build_fields(depth_limit, &mut do_write); + + // Error checking + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointer is valid. + match &mut tmp.field_decls { + None => panic!("Expected some field_decls 1"), + Some(field_decls) => { + let mut j = 0; + while j < field_decls.len() { + field_decls[j].var_name = String::from("false"); + j += 1; + } + } + } + } + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + tmp + } + RepType::PrimArray(p_type) => { + TopLevlDecl { + map, + var_name: var_name.clone(), + dec_type: format!("{}[]", p_type), + rep_type: String::from("hashcode"), + key: None, + field_decls: None, + contents: Some(ArrayContents { + map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", p_type), + rep_type: format!("{}[]", p_type), + enclosing_var: var_name.clone(), + key: None, + sub_contents: None, + }), // Ready to write this var_decl. + } + } + RepType::HashCodeArray(ty_string) => { + let mut tmp = TopLevlDecl { + map, + var_name: var_name.clone(), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode"), + key: Some(ty_string.clone()), + field_decls: None, + contents: Some(ArrayContents { + map, + var_name: format!("{}[..]", var_name), + dec_type: format!("{}[]", ty_string), + rep_type: String::from("hashcode[]"), + enclosing_var: var_name.clone(), + key: Some(ty_string.clone()), + sub_contents: Some(Vec::new()), + }), + }; + match &mut tmp.contents { + None => panic!(""), + Some(contents) => { + contents.build_contents(depth_limit - 1, &mut do_write); + + // Error checking: note for this and similar, tmp.contents valid is equivalent to tmp valid, if we have Vec of enums, contents is pointers. + if !do_write { + // Any "fields" are invalid, but tmp could be an enum/union and pointers is valid. + match &mut contents.sub_contents { + None => panic!("Expected some field_decls 1"), + Some(sub_contents) => { + let mut j = 0; + while j < sub_contents.len() { + sub_contents[j].var_name = String::from("false"); + j += 1; + } + } + } + } + + if ty_string.starts_with("Option") || ty_string.starts_with("Result") { + // this record is also invalid + tmp.var_name = String::from("false"); + } + } + } + tmp + } + }; + var_decls.push(toplevl_decl); + i += 1; + } + + var_decls +} + +impl<'a> Visitor<'a> for DaikonDeclsVisitor<'a> { + // Process a new function and write it to the decls file. + fn visit_fn(&mut self, fk: FnKind<'a>, _span: rustc_span::Span, _id: rustc_ast::NodeId) { + match &fk { + FnKind::Fn(_, _, f) => { + if !f.ident.as_str().starts_with("dtrace") { + let ppt_name = String::from(f.ident.as_str()); + write_entry(ppt_name.clone()); + let param_to_block_idx = map_params(&f.sig.decl); + let mut param_decls = grok_fn_sig(&f.sig.decl, self.map, self.depth_limit); + let mut i = 0; + while i < param_decls.len() { + param_decls[i].write(); + i += 1; + } + write_newline(); + match &f.body { + None => {} + Some(body) => { + // By now, all exit ppts are + // explicit Semi(Ret) stmts. + self.grok_fn_body( + ppt_name.clone(), + body, + &mut param_decls, + param_to_block_idx, + &f.sig.decl.output, + ); + } + } + } + } + _ => {} + } + visit::walk_fn(self, fk); + } +} + +// Lock on the decls file. +static DECLS: LazyLock>> = LazyLock::new(|| Mutex::new(dtrace_open())); + +// Open the decls file. +fn dtrace_open() -> Option { + let decls_path = format!("{}{}", *OUTPUT_NAME.lock().unwrap(), ".decls"); + let decls = std::path::Path::new(&decls_path); + Some(std::fs::File::options().write(true).append(true).open(&decls).unwrap()) +} + pub struct MacroExpander<'a, 'b> { pub cx: &'a mut ExtCtxt<'b>, monotonic: bool, // cf. `cx.monotonic_expander()` @@ -465,6 +1930,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { MacroExpander { cx, monotonic } } + #[allow(rustc::default_hash_types)] pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate { let file_path = match self.cx.source_map().span_to_filename(krate.spans.inner_span) { FileName::Real(name) => name @@ -480,6 +1946,29 @@ impl<'a, 'b> MacroExpander<'a, 'b> { dir_path, }); let krate = self.fully_expand_fragment(AstFragment::Crate(krate)).make_crate(); + // Decls pass. + // First, pass through the entire krate building HashMap> + // (value is always an ItemKind::Struct) + // Create new decls/dtrace files. Open decls file for writing. + // Visit the entire immutable AST with a non-mutable visitor to write the decls file, + // skipping over functions we generated. + if *DO_VISITOR.lock().unwrap() { + let mut struct_map: HashMap> = HashMap::new(); + let mut map_builder = DeclsHashMapBuilder { map: &mut struct_map }; + map_builder.visit_crate(&krate); + + // create or overwrite decls/dtrace + let decls_path = format!("{}{}", *OUTPUT_NAME.lock().unwrap(), ".decls"); + let decls = std::path::Path::new(&decls_path); + std::fs::File::create(&decls).unwrap(); + let dtrace_path = format!("{}{}", *OUTPUT_NAME.lock().unwrap(), ".dtrace"); + let dtrace = std::path::Path::new(&dtrace_path); + std::fs::File::create(&dtrace).unwrap(); + write_header(); + write_newline(); + let mut decls_visitor = DaikonDeclsVisitor { map: &struct_map, depth_limit: 4 }; // off by one to match dtrace + decls_visitor.visit_crate(&krate); + } assert_eq!(krate.id, ast::CRATE_NODE_ID); self.cx.trace_macros_diag(); krate diff --git a/compiler/rustc_parse/src/parser/daikon_strs.rs b/compiler/rustc_parse/src/parser/daikon_strs.rs new file mode 100644 index 0000000000000..ed27f96b5a520 --- /dev/null +++ b/compiler/rustc_parse/src/parser/daikon_strs.rs @@ -0,0 +1,1328 @@ +use crate::parser::item::OUTPUT_NAME; + +// Proper primitive types +// i8, i16, i32, i64, i128 and isize +// u8, u16, u32, u64, u128 and usize +// f32, f64 +// char +// bool +// () -- why is this possible for parameters :/ + +pub static I8: &str = "i8"; +pub static I16: &str = "i16"; +pub static I32: &str = "i32"; +pub static I64: &str = "i64"; +pub static I128: &str = "i128"; +pub static ISIZE: &str = "isize"; + +pub static U8: &str = "u8"; +pub static U16: &str = "u16"; +pub static U32: &str = "u32"; +pub static U64: &str = "u64"; +pub static U128: &str = "u128"; +pub static USIZE: &str = "usize"; + +pub static F32: &str = "f32"; +pub static F64: &str = "f64"; + +pub static CHAR: &str = "char"; +pub static BOOL: &str = "bool"; +pub static UNIT: &str = "()"; +pub static STR: &str = "str"; +pub static STRING: &str = "String"; +pub static VEC: &str = "Vec"; + +// placeholders are between the strs + +pub(crate) static INIT_NONCE: &str = "fn __skip() { let mut __daikon_nonce = 0;\nlet mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap();\n__daikon_nonce = *__unwrap_nonce;\n*__unwrap_nonce += 1;\ndrop(__unwrap_nonce);\n }"; +pub(crate) fn init_nonce() -> String { + String::from(INIT_NONCE) +} + +// TODO: before build_entry, initialize __daikon_nonce with the NONCE_COUNTER lock. +pub(crate) static DTRACE_ENTRY: [&str; 2] = + ["fn __skip() { dtrace_entry(\"", ":::ENTER\", __daikon_nonce); }"]; +pub(crate) fn build_entry(ppt_name: String) -> String { + let mut res = String::from(DTRACE_ENTRY[0]); + res.push_str(&ppt_name); + res.push_str(DTRACE_ENTRY[1]); + res +} + +pub(crate) static DTRACE_EXIT: [&str; 3] = + ["fn __skip() { dtrace_exit(\"", ":::EXIT", "\", __daikon_nonce); }"]; +pub(crate) fn build_exit(ppt_name: String, exit_counter: usize) -> String { + let mut res = String::from(DTRACE_EXIT[0]); + res.push_str(&ppt_name); + res.push_str(DTRACE_EXIT[1]); + res.push_str(&exit_counter.to_string()); + res.push_str(DTRACE_EXIT[2]); + res +} + +pub(crate) static DTRACE_PRIM: [&str; 4] = + ["fn __skip() { dtrace_print_prim::<", ">(", ", String::from(\"", "\")); }"]; +pub(crate) fn build_prim(p_type: String, var_name: String) -> String { + let mut res = String::from(DTRACE_PRIM[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM[1]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM[2]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM[3]); + res +} + +pub(crate) static DTRACE_PRIM_RET: [&str; 2] = + ["fn __skip() { dtrace_print_prim::<", ">(__daikon_ret, String::from(\"return\")); }"]; +pub(crate) fn build_prim_ret(p_type: String) -> String { + let mut res = String::from(DTRACE_PRIM_RET[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_RET[1]); + res +} + +pub(crate) static DTRACE_PRIM_REF: [&str; 5] = [ + "fn __skip() { dtrace_print_prim::<", + ">(", + "::from_str(&", + ".to_string()).expect(\"Ok\"), String::from(\"", + "\")); }", +]; +pub(crate) fn build_prim_ref(p_type: String, var_name: String) -> String { + let mut res = String::from(DTRACE_PRIM_REF[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF[1]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF[2]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM_REF[3]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM_REF[4]); + res +} + +// Generic routine used to print all non-string primitive parameter and return +// values. +pub(crate) static DTRACE_PRIM_REF_RET: [&str; 3] = [ + "fn __skip() { dtrace_print_prim::<", + ">(", + "::from_str(&__daikon_ret.to_string()).expect(\"Ok\"), String::from(\"return\")); }", +]; +pub(crate) fn build_prim_ref_ret(p_type: String) -> String { + let mut res = String::from(DTRACE_PRIM_REF_RET[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF_RET[1]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF_RET[2]); + res +} + +pub(crate) static DTRACE_PRIM_TOSTRING: [&str; 3] = + ["fn __skip() { dtrace_print_string(", ".to_string(), String::from(\"", "\")); }"]; +pub(crate) fn build_prim_with_tostring(var_name: String) -> String { + // TODO: change name + let mut res = String::from(DTRACE_PRIM_TOSTRING[0]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM_TOSTRING[1]); + res.push_str(&var_name); + res.push_str(DTRACE_PRIM_TOSTRING[2]); + res +} + +pub(crate) static DTRACE_PRIM_TOSTRING_RET: &str = + "fn __skip() { dtrace_print_string(__daikon_ret.to_string(), String::from(\"return\")); }"; +pub(crate) fn build_prim_with_tostring_ret() -> String { + String::from(DTRACE_PRIM_TOSTRING_RET) +} + +pub(crate) static DTRACE_PRIM_FIELD_TOSTRING: [&str; 3] = + ["dtrace_print_string(self.", ".to_string(), format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_prim_field_tostring(field_name: String) -> String { + let mut res = String::from(DTRACE_PRIM_FIELD_TOSTRING[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_FIELD_TOSTRING[1]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_FIELD_TOSTRING[2]); + res +} + +// pub(crate) fn build_prim_with_to_string + +pub(crate) static DTRACE_PRIM_STRUCT: [&str; 4] = + ["dtrace_print_prim::<", ">(self.", ", format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_field_prim(p_type: String, field_name: String) -> String { + let mut res = String::from(DTRACE_PRIM_STRUCT[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_STRUCT[1]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_STRUCT[2]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_STRUCT[3]); + res +} + +// TODO: if you have Vec<&'a &'b i32>, you will probably have to make a new Vec like this +// to satisfy dtrace_print_prim_vec(v: &Vec). +pub(crate) static DTRACE_PRIM_REF_STRUCT: [&str; 5] = [ + "dtrace_print_prim::<", + ">(", + "::from_str(&self.", + ".to_string()).expect(\"Ok\"), format!(\"{}{}\", prefix, \".", + "\"));", +]; +pub(crate) fn build_field_prim_ref(p_type: String, field_name: String) -> String { + let mut res = String::from(DTRACE_PRIM_REF_STRUCT[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF_STRUCT[1]); + res.push_str(&p_type); + res.push_str(DTRACE_PRIM_REF_STRUCT[2]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_REF_STRUCT[3]); + res.push_str(&field_name); + res.push_str(DTRACE_PRIM_REF_STRUCT[4]); + res +} + +pub(crate) static DTRACE_USERDEF: [&str; 6] = [ + "fn __skip() { dtrace_print_pointer(", + " as *const _ as usize, String::from(\"", + "\"));\n", + ".dtrace_print_fields(", + ", String::from(\"", + "\")); }", +]; +pub(crate) fn build_userdef(var_name: String, depth_arg: i32) -> String { + let mut res = String::from(DTRACE_USERDEF[0]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[1]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[2]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[3]); + res.push_str(&String::from(depth_arg.to_string())); + res.push_str(DTRACE_USERDEF[4]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[5]); + res +} + +pub(crate) fn build_userdef_with_ampersand_access(var_name: String, depth_arg: i32) -> String { + let mut res = String::from(DTRACE_USERDEF[0]); + res.push_str(&format!("&{}", var_name)); + res.push_str(DTRACE_USERDEF[1]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[2]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[3]); + res.push_str(&String::from(depth_arg.to_string())); + res.push_str(DTRACE_USERDEF[4]); + res.push_str(&var_name); + res.push_str(DTRACE_USERDEF[5]); + res +} + +pub(crate) static DTRACE_USERDEF_RET: [&str; 2] = [ + "fn __skip() { dtrace_print_pointer(__daikon_ret as *const _ as usize, String::from(\"return\"));\n__daikon_ret.dtrace_print_fields(", + ", String::from(\"return\")); }", +]; +pub(crate) fn build_userdef_ret(depth_arg: i32) -> String { + let mut res = String::from(DTRACE_USERDEF_RET[0]); + res.push_str(&String::from(depth_arg.to_string())); + res.push_str(DTRACE_USERDEF_RET[1]); + res +} + +pub(crate) static DTRACE_USERDEF_RET_AMPERSAND: [&str; 2] = [ + "fn __skip() { dtrace_print_pointer(&__daikon_ret as *const _ as usize, String::from(\"return\"));\n__daikon_ret.dtrace_print_fields(", + ", String::from(\"return\")); }", +]; +pub(crate) fn build_userdef_ret_ampersand(depth_arg: i32) -> String { + let mut res = String::from(DTRACE_USERDEF_RET_AMPERSAND[0]); + res.push_str(&String::from(depth_arg.to_string())); + res.push_str(DTRACE_USERDEF_RET_AMPERSAND[1]); + res +} + +pub(crate) static DTRACE_USERDEF_STRUCT: [&str; 5] = [ + "dtrace_print_pointer(self.", + " as *const _ as usize, format!(\"{}{}\", prefix, \".", + "\"));\nself.", + ".dtrace_print_fields(depth - 1, format!(\"{}{}\", prefix, \".", + "\"));", +]; +pub(crate) fn build_field_userdef(field_name: String) -> String { + let mut res = String::from(DTRACE_USERDEF_STRUCT[0]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT[1]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT[2]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT[3]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT[4]); + res +} + +pub(crate) static DTRACE_USERDEF_STRUCT_AMPERSAND: [&str; 5] = [ + "dtrace_print_pointer(&self.", + " as *const _ as usize, format!(\"{}{}\", prefix, \".", + "\"));\nself.", + ".dtrace_print_fields(depth - 1, format!(\"{}{}\", prefix, \".", + "\"));", +]; +pub(crate) fn build_field_userdef_with_ampersand_access(field_name: String) -> String { + let mut res = String::from(DTRACE_USERDEF_STRUCT_AMPERSAND[0]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT_AMPERSAND[1]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT_AMPERSAND[2]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT_AMPERSAND[3]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_STRUCT_AMPERSAND[4]); + res +} + +// always with ampersand, we will always make a copy. +pub(crate) static DTRACE_USERDEF_VEC_FIELDS: [&str; 3] = + ["::dtrace_print_fields_vec(&", ", depth - 1, format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_print_vec_fields_userdef( + plain_struct: String, + tmp_vec_name: String, + field_name: String, +) -> String { + let mut res = String::from(&plain_struct); + res.push_str(DTRACE_USERDEF_VEC_FIELDS[0]); + res.push_str(&tmp_vec_name); + res.push_str(DTRACE_USERDEF_VEC_FIELDS[1]); + res.push_str(&field_name); + res.push_str(DTRACE_USERDEF_VEC_FIELDS[2]); + res +} + +pub(crate) static DTRACE_PRINT_XFIELD_VEC: [&str; 4] = + ["::dtrace_print_", "_vec(&", ", format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_print_xfield_vec( + plain_struct: String, + field_name: String, + tmp_vec_name: String, +) -> String { + let mut res = String::from(&plain_struct); + res.push_str(DTRACE_PRINT_XFIELD_VEC[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELD_VEC[1]); + res.push_str(&tmp_vec_name); + res.push_str(DTRACE_PRINT_XFIELD_VEC[2]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELD_VEC[3]); + res +} + +pub(crate) static DTRACE_PRINT_POINTER_VEC_USERDEF: [&str; 4] = + ["dtrace_print_pointer_vec::<", ">(&", ", format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_print_pointer_vec_userdef( + plain_struct: String, + tmp_vec_name: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_PRINT_POINTER_VEC_USERDEF[0]); + res.push_str(&plain_struct); + res.push_str(DTRACE_PRINT_POINTER_VEC_USERDEF[1]); + res.push_str(&tmp_vec_name); + res.push_str(DTRACE_PRINT_POINTER_VEC_USERDEF[2]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_POINTER_VEC_USERDEF[3]); + res +} + +// we're expecting a tmp vec loop before this. +pub(crate) static DTRACE_VEC_POINTER: [&str; 3] = + ["dtrace_print_pointer(", ".as_ptr() as usize, String::from(\"", "\"));"]; +pub(crate) fn build_pointer_vec(var_name: String) -> String { + let mut res = String::from(DTRACE_VEC_POINTER[0]); + res.push_str(&var_name); + res.push_str(DTRACE_VEC_POINTER[1]); + res.push_str(&var_name); + res.push_str(DTRACE_VEC_POINTER[2]); + res +} + +pub(crate) static DTRACE_VEC_POINTER_RET: &str = + "dtrace_print_pointer(__daikon_ret.as_ptr() as usize, String::from(\"return\"));"; +pub(crate) fn build_pointer_vec_ret() -> String { + String::from(DTRACE_VEC_POINTER_RET) +} + +pub(crate) static DTRACE_PRINT_POINTER_VEC: [&str; 4] = [ + "dtrace_print_pointer_vec::<", + ">(&", + ", format!(\"{}{}\", String::from(\"", + "\"), \"[..]\"));", +]; +pub(crate) fn build_print_pointer_vec( + basic_type: String, + tmp_name: String, + var_name: String, +) -> String { + let mut res = String::from(DTRACE_PRINT_POINTER_VEC[0]); + res.push_str(&basic_type); + res.push_str(DTRACE_PRINT_POINTER_VEC[1]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_POINTER_VEC[2]); + res.push_str(&var_name); + res.push_str(DTRACE_PRINT_POINTER_VEC[3]); + res +} + +pub(crate) static DTRACE_VEC_FIELDS: [&str; 3] = + ["::dtrace_print_fields_vec(&", ", 3, format!(\"{}{}\", String::from(\"", "\"), \"[..]\")); }"]; // we always mash with a tmp loop, so just close __skip. +pub(crate) fn build_print_vec_fields( + plain_struct: String, + tmp_vec_name: String, + var_name: String, +) -> String { + let mut res = plain_struct.clone(); + res.push_str(DTRACE_VEC_FIELDS[0]); + res.push_str(&tmp_vec_name); + res.push_str(DTRACE_VEC_FIELDS[1]); + res.push_str(&var_name); + res.push_str(DTRACE_VEC_FIELDS[2]); + res +} + +pub(crate) static DAIKON_TMP_VEC_USERDEF: [&str; 8] = [ + "let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < v.len() { __daikon_tmp", + ".push(v[__daikon_tmp", + "]); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_daikon_tmp_vec_userdef( + first_tmp: String, + basic_type: String, + next_tmp: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC_USERDEF[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[1]); + res.push_str(&basic_type); + res.push_str(DAIKON_TMP_VEC_USERDEF[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[4]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[5]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[6]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF[7]); + res +} + +pub(crate) static DAIKON_TMP_VEC_USERDEF_FIELD: [&str; 9] = [ + "let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < v.len() { __daikon_tmp", + ".push(v[__daikon_tmp", + "].", + "); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_daikon_tmp_vec_field_userdef( + first_tmp: String, + field_type: String, + next_tmp: String, + field_name: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC_USERDEF_FIELD[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[1]); + res.push_str(&field_type); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[4]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[5]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[6]); + res.push_str(&field_name); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[7]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD[8]); + res +} + +pub(crate) static DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND: [&str; 9] = [ + "let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < v.len() { __daikon_tmp", + ".push(&v[__daikon_tmp", + "].", + "); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_daikon_tmp_vec_field_userdef_ampersand( + first_tmp: String, + field_type: String, + next_tmp: String, + field_name: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[1]); + res.push_str(&field_type); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[4]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[5]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[6]); + res.push_str(&field_name); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[7]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_USERDEF_FIELD_AMPERSAND[8]); + res +} + +// this will always be mashed with some subsequent call, so don't close __skip yet. +// the thing you mash it with must close __skip. +pub(crate) static DAIKON_TMP_VEC: [&str; 10] = [ + "fn __skip() { let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < ", + ".len() { __daikon_tmp", + ".push(", + "[__daikon_tmp", + "]); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_daikon_tmp_vec( + first_tmp: String, + basic_type: String, + next_tmp: String, + var_name: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC[1]); + res.push_str(&basic_type); + res.push_str(DAIKON_TMP_VEC[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC[4]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC[5]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC[6]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC[7]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC[8]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC[9]); + res +} + +// TODO: use this for params/returns where you have Vec. +pub(crate) static DAIKON_TMP_VEC_AMPERSAND: [&str; 10] = [ + "fn __skip() { let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < ", + ".len() { __daikon_tmp", + ".push(&", // for Vec, need to take &. + "[__daikon_tmp", + "]); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_daikon_tmp_vec_ampersand( + first_tmp: String, + basic_type: String, + next_tmp: String, + var_name: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC_AMPERSAND[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[1]); + res.push_str(&basic_type); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[4]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[5]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[6]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[7]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[8]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_AMPERSAND[9]); + res +} + +pub(crate) static DAIKON_TMP_VEC_PRIM: [&str; 11] = [ + "fn __skip() { let mut __daikon_tmp", + ": Vec<", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < ", + ".len() {__daikon_tmp", + ".push(", + "::from_str(&", + "[__daikon_tmp", + "].to_string()).expect(\"Ok\")); __daikon_tmp", + " += 1; }", +]; // don't close __skip() because we will mash +pub(crate) fn build_tmp_vec_prim( + first_tmp: String, + p_type: String, + next_tmp: String, + var_name: String, +) -> String { + let mut res = String::from(DAIKON_TMP_VEC_PRIM[0]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[1]); + res.push_str(&p_type); + res.push_str(DAIKON_TMP_VEC_PRIM[2]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[3]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[4]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC_PRIM[5]); + res.push_str(&first_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[6]); + res.push_str(&p_type); + res.push_str(DAIKON_TMP_VEC_PRIM[7]); + res.push_str(&var_name); + res.push_str(DAIKON_TMP_VEC_PRIM[8]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[9]); + res.push_str(&next_tmp); + res.push_str(DAIKON_TMP_VEC_PRIM[10]); + res +} + +pub(crate) static DTRACE_TMP_VEC_FOR_FIELD: [&str; 10] = [ + "let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < self.", + ".len() { __daikon_tmp", + ".push(self.", + "[__daikon_tmp", + "]); __daikon_tmp", + " += 1 }", +]; +pub(crate) fn build_tmp_vec_for_field( + first_tmp: String, + basic_type: String, + next_tmp: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_TMP_VEC_FOR_FIELD[0]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[1]); + res.push_str(&basic_type); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[2]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[3]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[4]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[5]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[6]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[7]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[8]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD[9]); + res +} + +// TODO: use this for fields which are f: Vec or f: &Vec, need to use &. +pub(crate) static DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND: [&str; 10] = [ + "let mut __daikon_tmp", + ": Vec<&", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < self.", + ".len() { __daikon_tmp", + ".push(&self.", // for f: Vec. + "[__daikon_tmp", + "]); __daikon_tmp", + " += 1 }", +]; +pub(crate) fn build_tmp_vec_for_field_ampersand( + first_tmp: String, + basic_type: String, + next_tmp: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[0]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[1]); + res.push_str(&basic_type); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[2]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[3]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[4]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[5]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[6]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[7]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[8]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_VEC_FOR_FIELD_AMPERSAND[9]); + res +} + +pub(crate) static DTRACE_POINTER_VEC_USERDEF: [&str; 3] = + ["dtrace_print_pointer(self.", ".as_ptr() as usize, format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_pointer_vec_userdef(field_name: String) -> String { + let mut res = String::from(DTRACE_POINTER_VEC_USERDEF[0]); + res.push_str(&field_name); + res.push_str(DTRACE_POINTER_VEC_USERDEF[1]); + res.push_str(&field_name); + res.push_str(DTRACE_POINTER_VEC_USERDEF[2]); + res +} + +pub(crate) static DTRACE_POINTER_ARR_USERDEF: [&str; 3] = [ + "dtrace_print_pointer(self.", + " as *const _ as *const () as usize, format!(\"{}{}\", prefix, \".", + "\"));", +]; +pub(crate) fn build_pointer_arr_userdef(field_name: String) -> String { + let mut res = String::from(DTRACE_POINTER_ARR_USERDEF[0]); + res.push_str(&field_name); + res.push_str(DTRACE_POINTER_ARR_USERDEF[1]); + res.push_str(&field_name); + res.push_str(DTRACE_POINTER_ARR_USERDEF[2]); + res +} + +pub(crate) static DTRACE_POINTERS_VEC_USERDEF: [&str; 4] = + ["dtrace_print_pointer_vec::<", ">(&", ", format!(\"{}{}[..]\", prefix, \".", "\"));"]; +pub(crate) fn build_pointers_vec_userdef( + basic_type: String, + tmp_name: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_POINTERS_VEC_USERDEF[0]); + res.push_str(&basic_type); + res.push_str(DTRACE_POINTERS_VEC_USERDEF[1]); + res.push_str(&tmp_name); + res.push_str(DTRACE_POINTERS_VEC_USERDEF[2]); + res.push_str(&field_name); + res.push_str(DTRACE_POINTERS_VEC_USERDEF[3]); + res +} + +pub(crate) static DTRACE_PRINT_FIELDS_FOR_FIELD: [&str; 3] = + ["::dtrace_print_fields_vec(&", ", depth - 1, format!(\"{}{}[..]\", prefix, \".", "\"));"]; +pub(crate) fn build_print_vec_fields_for_field( + basic_type: String, + tmp_name: String, + field_name: String, +) -> String { + let mut res = basic_type.clone(); + res.push_str(DTRACE_PRINT_FIELDS_FOR_FIELD[0]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_FIELDS_FOR_FIELD[1]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_FIELDS_FOR_FIELD[2]); + res +} + +pub(crate) static DTRACE_PRINT_XFIELD_FOR_FIELD_PROLOGUE: [&str; 2] = + ["pub fn dtrace_print_", "(&self, depth: i32, prefix: String) {"]; +pub(crate) fn build_dtrace_print_xfield_prologue(field_name: String) -> String { + let mut res = String::from(DTRACE_PRINT_XFIELD_FOR_FIELD_PROLOGUE[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELD_FOR_FIELD_PROLOGUE[1]); + res +} + +pub(crate) static DTRACE_PRINT_XFIELD_FOR_FIELD_MID: &str = "if depth == 0 { return; }"; +pub(crate) fn build_dtrace_print_xfield_middle() -> String { + String::from(DTRACE_PRINT_XFIELD_FOR_FIELD_MID) +} + +pub(crate) static DTRACE_PRINT_XFIELD_FOR_FIELD_EPILOGUE: &str = "}"; +pub(crate) fn build_dtrace_print_xfield_epilogue() -> String { + String::from(DTRACE_PRINT_XFIELD_FOR_FIELD_EPILOGUE) +} + +pub(crate) static DTRACE_TMP_PRIM_VEC_FOR_FIELD: [&str; 11] = [ + "let mut __daikon_tmp", + ": Vec<", + "> = Vec::new(); let mut __daikon_tmp", + " = 0; while __daikon_tmp", + " < self.", + ".len() { __daikon_tmp", + ".push(", + "::from_str(&self.", + "[__daikon_tmp", + "].to_string()).expect(\"Ok\")); __daikon_tmp", + " += 1; }", +]; +pub(crate) fn build_tmp_prim_vec_for_field( + first_tmp: String, + p_type: String, + next_tmp: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_TMP_PRIM_VEC_FOR_FIELD[0]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[1]); + res.push_str(&p_type); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[2]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[3]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[4]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[5]); + res.push_str(&first_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[6]); + res.push_str(&p_type); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[7]); + res.push_str(&field_name); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[8]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[9]); + res.push_str(&next_tmp); + res.push_str(DTRACE_TMP_PRIM_VEC_FOR_FIELD[10]); + res +} + +pub(crate) static DTRACE_PRINT_PRIM_VEC_FOR_FIELD: [&str; 4] = + ["dtrace_print_prim_vec::<", ">(&", ", format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_print_prim_vec_for_field( + p_type: String, + tmp_name: String, + field_name: String, +) -> String { + let mut res = String::from(DTRACE_PRINT_PRIM_VEC_FOR_FIELD[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRINT_PRIM_VEC_FOR_FIELD[1]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_PRIM_VEC_FOR_FIELD[2]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_PRIM_VEC_FOR_FIELD[3]); + res +} + +pub(crate) static DTRACE_PRINT_STRING_VEC_FOR_FIELD: [&str; 3] = + ["dtrace_print_string_vec(&", ", format!(\"{}{}\", prefix, \".", "\"));"]; +pub(crate) fn build_print_string_vec_for_field(tmp_name: String, field_name: String) -> String { + let mut res = String::from(DTRACE_PRINT_STRING_VEC_FOR_FIELD[0]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_STRING_VEC_FOR_FIELD[1]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_STRING_VEC_FOR_FIELD[2]); + res +} + +pub(crate) static DTRACE_CALL_PRINT_FIELD: [&str; 2] = + ["self.dtrace_print_", "(depth, prefix.clone());"]; +pub(crate) fn build_call_print_field(field_name: String) -> String { + let mut res = String::from(DTRACE_CALL_PRINT_FIELD[0]); + res.push_str(&field_name); + res.push_str(DTRACE_CALL_PRINT_FIELD[1]); + res +} + +#[allow(dead_code)] +pub(crate) static DTRACE_PRINT_XFIELDS_VEC: [&str; 6] = ["pub fn dtrace_print_", + "_vec(v: &Vec<&", + ">, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"0x{:x} \", v[i].", + ".as_ptr() as usize)); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"0x{:x}\", v[i].", + ".as_ptr() as usize)); } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(traces, \"0\").ok(); }"]; +pub(crate) fn build_print_xfield_for_vec(field_name: String, basic_type: String) -> String { + let mut res = String::from(DTRACE_PRINT_XFIELDS_VEC[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_VEC[1]); + res.push_str(&basic_type); + res.push_str(DTRACE_PRINT_XFIELDS_VEC[2]); + res.push_str(&*OUTPUT_NAME.lock().unwrap()); + res.push_str(DTRACE_PRINT_XFIELDS_VEC[3]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_VEC[4]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_VEC[5]); + res +} + +pub(crate) static LET_RET: [&str; 3] = ["fn __skip() { let __daikon_ret: ", " = ", "; }"]; +pub(crate) fn build_let_ret(ret_ty: String, expr: String) -> String { + let mut res = String::from(LET_RET[0]); + res.push_str(&ret_ty); + res.push_str(LET_RET[1]); + res.push_str(&expr); + res.push_str(LET_RET[2]); + res +} + +pub(crate) static RET: [&str; 1] = ["fn __skip() { return __daikon_ret; }"]; +pub(crate) fn build_ret() -> String { + String::from(RET[0]) +} + +// you have to delete this? +// make this an array with DTRACE_PRINT_FIELDS_EPILOGUE... +pub(crate) static DTRACE_PRINT_FIELDS_PROLOGUE: &str = "impl __skip { pub fn dtrace_print_fields(&self, depth: i32, prefix: String) { if depth == 0 { return; } "; +pub(crate) fn dtrace_print_fields_prologue() -> String { + String::from(DTRACE_PRINT_FIELDS_PROLOGUE) +} + +pub(crate) static DTRACE_PRINT_FIELDS_EPILOGUE: &str = "} } struct __skip{}"; // maybe can avoid deleting it, but still bad +pub(crate) fn dtrace_print_fields_epilogue() -> String { + String::from(DTRACE_PRINT_FIELDS_EPILOGUE) +} + +pub(crate) static DTRACE_PRINT_FIELDS_VEC_PROLOGUE: [&str; 2] = [ + "impl __skip { pub fn dtrace_print_fields_vec(v: &Vec<&", + ">, depth: i32, prefix: String) { if depth == 0 { return; } ", +]; +pub(crate) fn dtrace_print_fields_vec_prologue(spliced_struct: String) -> String { + let mut res = String::from(DTRACE_PRINT_FIELDS_VEC_PROLOGUE[0]); + res.push_str(&spliced_struct); + res.push_str(DTRACE_PRINT_FIELDS_VEC_PROLOGUE[1]); + res +} + +pub(crate) static DTRACE_PRINT_FIELDS_VEC_EPILOGUE: &str = "} } struct __skip{}"; +pub(crate) fn dtrace_print_fields_vec_epilogue() -> String { + String::from(DTRACE_PRINT_FIELDS_VEC_EPILOGUE) +} + +pub(crate) static DTRACE_PRINT_XFIELDS_VEC_PROLOGUE: &str = "impl __skip {"; +pub(crate) fn dtrace_print_xfields_vec_prologue() -> String { + String::from(DTRACE_PRINT_XFIELDS_VEC_PROLOGUE) +} + +pub(crate) static DTRACE_PRINT_XFIELDS_VEC_EPILOGUE: &str = " }"; +pub(crate) fn dtrace_print_xfields_vec_epilogue() -> String { + String::from(DTRACE_PRINT_XFIELDS_VEC_EPILOGUE) +} + +pub(crate) static DTRACE_PRINT_XFIELDS: [&str; 6] = ["pub fn dtrace_print_", + "_vec(v: &Vec<&", + ">, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"{} \", v[i].", + ")); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"{}\", v[i].", + ")); } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(traces, \"0\").ok(); }"]; +pub(crate) fn build_print_xfield(field_name: String, basic_type: String) -> String { + let mut res = String::from(DTRACE_PRINT_XFIELDS[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS[1]); + res.push_str(&basic_type); + res.push_str(DTRACE_PRINT_XFIELDS[2]); + res.push_str(&*OUTPUT_NAME.lock().unwrap()); + res.push_str(DTRACE_PRINT_XFIELDS[3]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS[4]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS[5]); + res +} + +pub(crate) static DTRACE_PRINT_XFIELDS_STRING: [&str; 6] = ["pub fn dtrace_print_", + "_vec(v: &Vec<&", + ">, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"\\\"{}\\\" \", v[i].", + ")); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"\\\"{}\\\"\", v[i].", + ")); } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(traces, \"0\").ok(); }"]; +pub(crate) fn build_print_xfield_string(field_name: String, basic_type: String) -> String { + let mut res = String::from(DTRACE_PRINT_XFIELDS_STRING[0]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_STRING[1]); + res.push_str(&basic_type); + res.push_str(DTRACE_PRINT_XFIELDS_STRING[2]); + res.push_str(&*OUTPUT_NAME.lock().unwrap()); + res.push_str(DTRACE_PRINT_XFIELDS_STRING[3]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_STRING[4]); + res.push_str(&field_name); + res.push_str(DTRACE_PRINT_XFIELDS_STRING[5]); + res +} + +pub(crate) static BUILD_POINTER_ARR: [&str; 3] = + ["dtrace_print_pointer(", " as *const _ as *const () as usize, String::from(\"", "\"));"]; +pub(crate) fn build_pointer_arr(var_name: String) -> String { + let mut res = String::from(BUILD_POINTER_ARR[0]); + res.push_str(&var_name); + res.push_str(BUILD_POINTER_ARR[1]); + res.push_str(&var_name); + res.push_str(BUILD_POINTER_ARR[2]); + res +} + +// does this work for references? +pub(crate) static BUILD_POINTER_ARR_RET: &str = "dtrace_print_pointer(__daikon_ret as *const _ as *const () as usize, String::from(\"return\"));"; +pub(crate) fn build_pointer_arr_ret() -> String { + String::from(BUILD_POINTER_ARR_RET) +} + +pub(crate) static DTRACE_PRINT_FIELDS_NOOP: &str = + "pub fn dtrace_print_fields(&self, _depth: i32, _prefix: String) {}"; +pub(crate) fn build_dtrace_print_fields_noop() -> String { + String::from(DTRACE_PRINT_FIELDS_NOOP) +} + +pub(crate) static DTRACE_PRINT_FIELDS_VEC_NOOP: [&str; 2] = + ["pub fn dtrace_print_fields_vec(_v: &Vec<&", ">, _depth: i32, _prefix: String) {}"]; +pub(crate) fn build_dtrace_print_fields_vec_noop(basic_struct: String) -> String { + let mut res = String::from(DTRACE_PRINT_FIELDS_VEC_NOOP[0]); + res.push_str(&basic_struct); + res.push_str(DTRACE_PRINT_FIELDS_VEC_NOOP[1]); + res +} + +// only end fn __skip because we will smash a tmp vec loop on the front. +pub(crate) static DTRACE_PRINT_PRIM_VEC: [&str; 4] = + ["dtrace_print_prim_vec::<", ">(&", ", String::from(\"", "\")); }"]; +pub(crate) fn build_print_prim_vec(p_type: String, tmp_name: String, var_name: String) -> String { + let mut res = String::from(DTRACE_PRINT_PRIM_VEC[0]); + res.push_str(&p_type); + res.push_str(DTRACE_PRINT_PRIM_VEC[1]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_PRIM_VEC[2]); + res.push_str(&var_name); + res.push_str(DTRACE_PRINT_PRIM_VEC[3]); + res +} + +pub(crate) static DTRACE_PRINT_STRING_VEC: [&str; 3] = + ["dtrace_print_string_vec(&", ", String::from(\"", "\")); }"]; +pub(crate) fn build_print_string_vec(tmp_name: String, var_name: String) -> String { + let mut res = String::from(DTRACE_PRINT_STRING_VEC[0]); + res.push_str(&tmp_name); + res.push_str(DTRACE_PRINT_STRING_VEC[1]); + res.push_str(&var_name); + res.push_str(DTRACE_PRINT_STRING_VEC[2]); + res +} + +pub(crate) static BUILD_A_IMPL_BLOCK: &str = "impl __skip {}"; +pub(crate) fn base_impl() -> String { + String::from(BUILD_A_IMPL_BLOCK) +} + +pub(crate) static FABRICATE_TYPE_FOR_IMPL: [&str; 3] = ["fn __skip() -> ", " {}\nstruct ", "{}"]; +pub(crate) fn build_phony_ret(struct_name: String) -> String { + let mut res = String::from(FABRICATE_TYPE_FOR_IMPL[0]); + res.push_str(&struct_name); + res.push_str(FABRICATE_TYPE_FOR_IMPL[1]); + res.push_str(&struct_name); + res.push_str(FABRICATE_TYPE_FOR_IMPL[2]); + res +} + +pub(crate) static VOID_RETURN: &str = "fn __skip() { return; }"; +pub(crate) fn build_void_return() -> String { + String::from(VOID_RETURN) +} + +pub(crate) static DTRACE_NEWLINE: &str = "fn __skip() { dtrace_newline(); }"; +pub(crate) fn dtrace_newline() -> String { + String::from(DTRACE_NEWLINE) +} + +// this NONCE_COUNTER per-file is broken for multi-file non-concurrent programs. It has to be a single counter shared between all the files. +// Difficult in Rust as there is no easy extern escape like in C. Maybe unsafe. +pub(crate) static IMPORTS: &str = "use std::fs::File;\nuse std::io::prelude::*;\nuse std::sync::{LazyLock, Mutex};\nuse std::str::FromStr;\nstatic NONCE_COUNTER: LazyLock> = LazyLock::new(|| Mutex::new(0));"; +pub(crate) fn build_imports() -> String { + String::from(IMPORTS) +} + +pub(crate) static DAIKON_LIB: [&str; 15] = [ + "pub fn dtrace_print_pointer_arr(v: &[&T], var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"0x{:x} \", v[i] as *const _ as usize)); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"0x{:x}\", v[i] as *const _ as usize)); + } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +pub fn dtrace_print_pointer_vec(v: &Vec<&T>, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"0x{:x} \", v[i] as *const _ as usize)); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"0x{:x}\", v[i] as *const _ as usize)); + } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +// T must implement Display trait +fn dtrace_print_prim_arr(v: &[T], prefix: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", format!(\"{}{}\", prefix, \"[..]\")).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"{} \", v[i])); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"{}\", v[i])); + } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_print_prim_vec(v: &Vec, prefix: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", format!(\"{}{}\", prefix, \"[..]\")).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"{} \", v[i])); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"{}\", v[i])); + } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_print_str(v: &str, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + writeln!(&mut traces, \"{}\", v).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +// T must implement Display trait +fn dtrace_print_prim(v: T, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + writeln!(&mut traces, \"{}\", v).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_print_string(v: String, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + writeln!(&mut traces, \"\\\"{}\\\"\", v).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_print_string_vec(v: &Vec, prefix: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", format!(\"{}{}\", prefix, \"[..]\")).ok(); + let mut arr = String::from(\"[\"); + let mut i = 0; + while i+1 < v.len() { + arr.push_str(&format!(\"\\\"{}\\\" \", v[i])); + i += 1; + } + if v.len() > 0 { + arr.push_str(&format!(\"\\\"{}\\\"\", v[i])); + } + arr.push_str(\"]\"); + writeln!(&mut traces, \"{}\", arr).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_print_pointer(v: usize, var_name: String) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", var_name).ok(); + writeln!(&mut traces, \"0x{:x}\", v).ok(); + writeln!(&mut traces, \"0\").ok(); +} + +fn dtrace_entry_no_nonce(ppt_name: &str) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", ppt_name).ok(); +} + +fn dtrace_exit_no_nonce(ppt_name: &str) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", ppt_name).ok(); +} + +fn dtrace_entry(ppt_name: &str, nonce: u32) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(&mut traces, \"{}\", ppt_name).ok(); + writeln!(&mut traces, \"this_invocation_nonce\").ok(); + writeln!(&mut traces, \"{}\", nonce).ok(); +} + +fn dtrace_exit(ppt_name: &str, nonce: u32) { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(traces, \"{}\", ppt_name).ok(); + writeln!(traces, \"this_invocation_nonce\").ok(); + writeln!(traces, \"{}\", nonce).ok(); +} + +fn dtrace_newline() { + let mut traces = match File::options().append(true).open(\"", + ".dtrace\") { + Err(why) => panic!(\"Daikon couldn't open file, {}\", why), + Ok(traces) => traces, + }; + writeln!(traces, \"\").ok(); +}", +]; + +pub(crate) fn daikon_lib() -> String { + let mut res = String::from(DAIKON_LIB[0]); + for i in 1..DAIKON_LIB.len() { + res.push_str(&*OUTPUT_NAME.lock().unwrap()); + res.push_str(DAIKON_LIB[i]); + } + res +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index eb264f59fedbe..b22d3a0d4f602 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,8 +1,39 @@ use std::fmt::Write; use std::mem; +use crate::parser::daikon_strs::{ + BOOL, CHAR, F32, F64, I8, I16, I32, I64, I128, ISIZE, STR, STRING, U8, U16, U32, U64, U128, + UNIT, USIZE, VEC, base_impl, build_call_print_field, build_daikon_tmp_vec, + build_daikon_tmp_vec_ampersand, build_daikon_tmp_vec_field_userdef, + build_daikon_tmp_vec_field_userdef_ampersand, build_daikon_tmp_vec_userdef, + build_dtrace_print_fields_noop, build_dtrace_print_fields_vec_noop, + build_dtrace_print_xfield_epilogue, build_dtrace_print_xfield_middle, + build_dtrace_print_xfield_prologue, build_entry, build_exit, build_field_prim, + build_field_prim_ref, build_field_userdef, build_field_userdef_with_ampersand_access, + build_imports, build_let_ret, build_phony_ret, build_pointer_arr, build_pointer_arr_ret, + build_pointer_arr_userdef, build_pointer_vec, build_pointer_vec_ret, build_pointer_vec_userdef, + build_pointers_vec_userdef, build_prim, build_prim_field_tostring, build_prim_ref, + build_prim_ref_ret, build_prim_ret, build_prim_with_tostring, build_prim_with_tostring_ret, + build_print_pointer_vec, build_print_pointer_vec_userdef, build_print_prim_vec, + build_print_prim_vec_for_field, build_print_string_vec, build_print_string_vec_for_field, + build_print_vec_fields, build_print_vec_fields_for_field, build_print_vec_fields_userdef, + build_print_xfield, build_print_xfield_for_vec, build_print_xfield_string, + build_print_xfield_vec, build_ret, build_tmp_prim_vec_for_field, build_tmp_vec_for_field, + build_tmp_vec_for_field_ampersand, build_tmp_vec_prim, build_userdef, build_userdef_ret, + build_userdef_ret_ampersand, build_userdef_with_ampersand_access, build_void_return, + daikon_lib, dtrace_newline, dtrace_print_fields_epilogue, dtrace_print_fields_prologue, + dtrace_print_fields_vec_epilogue, dtrace_print_fields_vec_prologue, + dtrace_print_xfields_vec_epilogue, dtrace_print_xfields_vec_prologue, init_nonce, +}; +use crate::{StripTokens, new_parser_from_source_str, unwrap_or_emit_fatal}; +use rustc_ast::mut_visit::*; +use rustc_ast::*; +use std::collections::HashMap; +use std::io::Write as FileWrite; +use std::sync::{LazyLock, Mutex}; + use ast::token::IdentIsRaw; -use rustc_ast::ast::*; +// use rustc_ast::ast::*; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; @@ -25,6 +56,1817 @@ use super::{ use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField}; use crate::{exp, fluent_generated as fluent}; +// Stores the output prefix +// E.g., foo.rs -> foo, or the name of the cargo project. +pub static OUTPUT_NAME: LazyLock> = LazyLock::new(|| Mutex::new(String::from(""))); + +// True if we are not bootstrapping the standard library. +pub static DO_VISITOR: LazyLock> = LazyLock::new(|| Mutex::new(false)); + +// For generating unique names for auxiliary files to parse in parse_items_from_string. +static PARSER_COUNTER: LazyLock> = LazyLock::new(|| Mutex::new(0)); + +/* + Primary visitor pass for dtrace instrumentation. +*/ +struct DaikonDtraceVisitor<'a> { + // For parsing string fragments + pub parser: &'a Parser<'a>, + + // For appending impl blocks to the file + pub mod_items: &'a mut ThinVec>, +} + +// Represents a coarse-grained breakdown of any Rust type. +// Information about references is handled in get_basic_type. +// E.g., +// i32 -> Prim("i32") +// Vec -> PrimVec("char") +// &'a Vec -> UserDefVec("X") +// &[String] -> PrimArray("String") +// &'a &'b Widget -> UserDef("Widget") +// All enums, structs, and unions are categorized as UserDef, +// so we cannot distinguish between them after this point, +// and this forces us to implement noop dtrace routines +// for them. +// This can be fixed by doing a first pass to filter only +// structs which belong to the crate being compiled. Then +// we can appeal to a /tmp file at compile-time and skip +// enums, unions, and all UserDef types from outside the +// crate. +#[derive(PartialEq)] +enum BasicType { + Prim(String), + UserDef(String), + PrimVec(String), + UserDefVec(String), + PrimArray(String), + UserDefArray(String), + NoRet, + Error, +} + +// Given a pattern pat from a function signature representing a parameter name, +// return the argument name in a String. +fn get_param_ident(pat: &Box) -> String { + match &pat.kind { + PatKind::Ident(_mode, ident, None) => String::from(ident.as_str()), + _ => panic!("Formal arg does not have simple identifier"), + } +} + +// Given a reduced type String (i.e., no references or junk in front), +// check if the type String is a primitive (i32, u32, String, etc.) +// and return a BasicType representing it or BasicType::Error otherwise. +// i32 -> BasicType::Prim("i32") +// Vec -> BasicType::Error +fn check_prim(ty_str: &str) -> BasicType { + if ty_str == I8 { + return BasicType::Prim(String::from(I8)); + } else if ty_str == I16 { + return BasicType::Prim(String::from(I16)); + } else if ty_str == I32 { + return BasicType::Prim(String::from(I32)); + } else if ty_str == I64 { + return BasicType::Prim(String::from(I64)); + } else if ty_str == I128 { + return BasicType::Prim(String::from(I128)); + } else if ty_str == ISIZE { + return BasicType::Prim(String::from(ISIZE)); + } else if ty_str == U8 { + return BasicType::Prim(String::from(U8)); + } else if ty_str == U16 { + return BasicType::Prim(String::from(U16)); + } else if ty_str == U32 { + return BasicType::Prim(String::from(U32)); + } else if ty_str == U64 { + return BasicType::Prim(String::from(U64)); + } else if ty_str == U128 { + return BasicType::Prim(String::from(U128)); + } else if ty_str == USIZE { + return BasicType::Prim(String::from(USIZE)); + } else if ty_str == F32 { + return BasicType::Prim(String::from(F32)); + } else if ty_str == F64 { + return BasicType::Prim(String::from(F64)); + } else if ty_str == CHAR { + return BasicType::Prim(String::from(CHAR)); + } else if ty_str == BOOL { + return BasicType::Prim(String::from(BOOL)); + } else if ty_str == UNIT { + return BasicType::Prim(String::from(UNIT)); + } else if ty_str == STR { + return BasicType::Prim(String::from(STR)); + } else if ty_str == STRING { + return BasicType::Prim(String::from(STRING)); + } + BasicType::Error +} + +// is Vec with > 1 arg meaningful? +fn grok_vec_args(path: &Path, is_ref: &mut bool) -> BasicType { + // Reset in case we have an &Vec, since we want to know if + // the Vec arguments are references are not, i.e., Vec vs. + // Vec<&X>. + *is_ref = false; + match &path.segments[path.segments.len() - 1].args { + None => BasicType::Error, + Some(args) => match &**args { + GenericArgs::AngleBracketed(brack_args) => match &brack_args.args[0] { + AngleBracketedArg::Arg(arg) => match &arg { + GenericArg::Type(arg_type) => match &get_basic_type(&arg_type.kind, is_ref) { + BasicType::Prim(p_type) => BasicType::PrimVec(String::from(p_type)), + BasicType::UserDef(basic_type) => { + BasicType::UserDefVec(String::from(basic_type)) + } + _ => BasicType::Error, + }, + _ => BasicType::Error, + }, + _ => BasicType::Error, + }, + _ => BasicType::Error, + }, + } +} + +// Used to reduce a String like X to X. +// Not necessary in any cases I have found, but it is +// used to make calls like +// X::dtrace_print_... rather than +// X::dtrace_print... +// For Vec. +fn cut_lifetimes(spliced_struct: String) -> String { + let mut res = String::from(""); + let mut i = 0; + while i < spliced_struct.len() { + if spliced_struct.chars().nth(i).unwrap() == '<' { + return res; + } + res.push_str(&String::from(spliced_struct.chars().nth(i).unwrap())); + i += 1; + } + res +} + +// If there is no output file specified with -o and we have not +// been invoked by cargo, take the OUTPUT_NAME from the input file +// name. +// foo.rs -> foo +pub fn jot_output_name(s: String) { + let end = match s.rfind(".") { + // .rs + None => panic!("no . at the end of input file name"), + Some(end) => end, + }; + let mut start = match s.rfind("/") { + // .../.rs + None => 0, + Some(slash) => slash + 1, + }; + let mut res = String::from(""); + while start < end { + res.push_str(&format!("{}", s.chars().nth(start).unwrap())); + start += 1; + } + *OUTPUT_NAME.lock().unwrap() = res; +} + +// Hack. +// Given a pretty-printed struct: +// struct X { +// ... +// } +// Splice the name X from this String. This is the only way +// I have found to take a struct Item and obtain its identifier +// including generics in a String. +// The identifier plus generics are required to synthesize impl blocks +// with dtrace routines: +// impl X { +// dtrace_print_fields(self, ...) +// } +// This method is broken in many cases. Pretty-printing can include triple-bar +// comments or attributes. +// Indeed, the pretty-printed String could be as bad as: +/* + +/// This is an awesome struct +#[cfg(test)] +struct X { + ... +} + +*/ +fn splice_struct(pp_struct: &String, stop: &mut bool) -> String { + let start_idx = pp_struct.find(" "); + match &start_idx { + None => panic!("Can't find space in pp_struct"), + Some(idx) => { + let bound = pp_struct.find("{"); + match &bound { + None => { + *stop = true; + String::from("") + } + Some(bound) => { + let mut i = idx + 1; + let mut res = String::from(""); + while i < *bound - 1 { + res.push_str(&String::from(pp_struct.chars().nth(i).unwrap())); + i += 1; + } + // don't forget pub struct + if res.starts_with("struct") { + return res[7..].to_string(); + } else if res.starts_with("enum") { + return res[5..].to_string(); + } else if res.starts_with("union") { + return res[6..].to_string(); + } + res + } + } + } + } +} + +// Given a Rust type, break it down into something that fits into +// BasicType. If it is a reference, note this with is_ref. +// For Vec/array, is_ref indicates whether the contents of the +// container are references or not rather than the container +// itself. It does not matter if the container is_ref or not, +// since we always make a copy Vec with references to contents. +fn get_basic_type(kind: &TyKind, is_ref: &mut bool) -> BasicType { + match &kind { + TyKind::Array(arr_type, _anon_const) => match &get_basic_type(&arr_type.kind, is_ref) { + BasicType::Prim(p_type) => BasicType::PrimArray(String::from(p_type)), + BasicType::UserDef(basic_type) => BasicType::UserDefArray(String::from(basic_type)), + _ => panic!("higher-dim arrays not supported"), + }, + TyKind::Slice(arr_type) => match &get_basic_type(&arr_type.kind, is_ref) { + BasicType::Prim(p_type) => BasicType::PrimArray(String::from(p_type)), + BasicType::UserDef(basic_type) => BasicType::UserDefArray(String::from(basic_type)), + _ => panic!("higher-dim arrays not supported"), + }, + TyKind::Ptr(_mut_ty) => BasicType::Error, + TyKind::Ref(_, mut_ty) => { + *is_ref = true; + // recurse to get to the underlying type + get_basic_type(&mut_ty.ty.kind, is_ref) + } + TyKind::Path(_, path) => { + if path.segments.len() == 0 { + panic!("Path has no type"); + } + let ty_string = path.segments[path.segments.len() - 1].ident.as_str(); + let try_prim = check_prim(ty_string); + if try_prim != BasicType::Error { + return try_prim; + } + if ty_string == VEC { + return grok_vec_args(&path, is_ref); + } + // Return full type: BasicType, need generics in some cases. + BasicType::UserDef(ty_string.to_string()) + } + _ => BasicType::Error, + } +} + +// Unused. This was intended to allow easy invalidation +// of parameters. E.g., if parameter x was invalidated with +// drop(x), we need to know which idx it belongs to in our +// Vec of dtrace information to avoid logging it at future +// exit ppts. +// Parameter invalidation is still unimplemented. +#[allow(rustc::default_hash_types)] +fn map_params(decl: &Box) -> HashMap { + let mut res = HashMap::new(); + let mut i = 0; + while i < decl.inputs.len() { + res.insert(get_param_ident(&decl.inputs[i].pat), i as i32); + i += 1; + } + res +} + +// Used to check if the last statement in each function is an +// explicit void return. If it is not, an explicit void return +// is added at the end to make it easy to identify the final +// exit ppt. +// This does not detect something like +/* +if cond { return; } else { return; } +*/ +// In this case, an extra void return is unreachable. +fn last_stmt_is_void_return(block: &Box) -> bool { + if block.stmts.len() == 0 { + panic!("no stmts to check"); + } + match &block.stmts[block.stmts.len() - 1].kind { + StmtKind::Semi(semi) => match &semi.kind { + ExprKind::Ret(None) => true, + _ => false, + }, + _ => false, + } +} + +impl<'a> DaikonDtraceVisitor<'a> { + // Given a block of stmts in a String and a block, parse the stmts + // into a Vec, and append all stmts to the block. + fn append_to_block(&self, stuff: String, block: &mut Box) { + match &self.parser.parse_items_from_string(stuff.clone()) { + Err(_why) => panic!("Parsing internal String failed"), + Ok(items) => match &items[0].kind { + ItemKind::Fn(wrapper) => match &wrapper.body { + None => panic!("No body to insert"), + Some(body) => { + for stmt in body.stmts.clone() { + block.stmts.push(stmt.clone()); + } + } + }, + _ => panic!("Expected Fn in append_to_block"), + }, + } + } + + // Given a block of stmts in a String, a block, and an idx into the block, + // parse the stmts into a Vec, and insert all stmts at the specified index. + fn insert_into_block(&self, loc: usize, stuff: String, block: &mut Box) -> usize { + let mut i = loc; + let items = self.parser.parse_items_from_string(stuff.clone()); + match &items { + Err(_why) => panic!("Internal String parsing failed"), + Ok(items) => match &items[0].kind { + ItemKind::Fn(wrapper) => match &wrapper.body { + None => panic!("No body to insert"), + Some(body) => { + for stmt in body.stmts.clone() { + block.stmts.insert(i, stmt.clone()); + i += 1; + } + } + }, + _ => panic!("Internal Daikon str is malformed"), + }, + } + i + } + + // Take an if stmt and use invariants about if stmts + // to walk all blocks and locate exit ppts. + // expr: If expression + // exit_counter: gives the previously seen number of exit ppts + // ppt_name: ppt name + // dtrace_param_blocks: Vec of String blocks, with the ith block + // giving the dtrace calls needed to log the ith parameter. + // param_to_block_idx: map of param identifiers to idx into + // dtrace_param_blocks + // ret_ty: return type of the function + // daikon_tmp_counter: gives the number of previously allocated + // temporaries added into the code. + #[allow(rustc::default_hash_types)] + fn grok_expr_for_if( + &mut self, + expr: &mut Box, + exit_counter: &mut usize, + ppt_name: String, + dtrace_param_blocks: &mut Vec, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + daikon_tmp_counter: &mut u32, + ) { + match &mut expr.kind { + ExprKind::Block(block, _) => { + self.grok_block( + ppt_name.clone(), + block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + } + ExprKind::If(_, if_block, None) => { + self.grok_block( + ppt_name.clone(), + if_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + } + ExprKind::If(_, if_block, Some(another_expr)) => { + self.grok_block( + ppt_name.clone(), + if_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + self.grok_expr_for_if( + another_expr, + exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + daikon_tmp_counter, + ); + } + _ => panic!("Internal error handling if stmt with else!"), + } + } + + // Given a ret_expr from an explicit return stmt or a non-semi + // trailing return, insert code into body at index i to log the + // ret_expr. + // i: index into block to insert logging + // ret_expr: Expr representing the return value at a given exit ppt + // body: the block to insert into + // exit_counter: label for the next exit ppt + // ppt_name: program point name + // dtrace_param_blocks: Vec of logging code stored in Strings + // ret_ty: return type of the function + // daikon_tmp_counter: label for the next temporary variable + fn build_return( + &mut self, + i: &mut usize, + ret_expr: &Expr, // &Box? + body: &mut Box, + exit_counter: &mut usize, + ppt_name: String, + dtrace_param_blocks: &mut Vec, + ret_ty: &FnRetTy, + daikon_tmp_counter: &mut u32, + ) { + let exit = build_exit(ppt_name.clone(), *exit_counter); + *exit_counter += 1; + + *i = self.insert_into_block(*i, exit.clone(), body); + + for param_block in &mut *dtrace_param_blocks { + *i = self.insert_into_block(*i, param_block.clone(), body); + } + + let mut ret_is_ref = false; + let r_ty = match &ret_ty { + FnRetTy::Default(_span) => BasicType::NoRet, + FnRetTy::Ty(ty) => get_basic_type(&ty.kind, &mut ret_is_ref), + }; + let pr_ty = match &ret_ty { + FnRetTy::Ty(ty) => ty, + _ => panic!("Inconsistent return type"), + }; + // Process return expr + let expr = pprust::expr_to_string(&ret_expr); + let ret_let = build_let_ret(pprust::ty_to_string(&pr_ty), expr.clone()); + *i = self.insert_into_block(*i, ret_let, body); + match &r_ty { + BasicType::Prim(p_type) => { + let prim_record_ret = if p_type == "String" || p_type == "str" { + build_prim_with_tostring_ret() + } else if ret_is_ref { + build_prim_ref_ret(p_type.clone()) + } else { + build_prim_ret(p_type.clone()) + }; + *i = self.insert_into_block(*i, prim_record_ret, body); + } + BasicType::UserDef(_) => { + if ret_is_ref == false { + let userdef_record_ret = build_userdef_ret_ampersand(3); + *i = self.insert_into_block(*i, userdef_record_ret, body); + } else { + let userdef_record_ret = build_userdef_ret(3); + *i = self.insert_into_block(*i, userdef_record_ret, body); + } + } + BasicType::PrimVec(p_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec( + format!("__daikon_tmp{}", first_tmp), + String::from("return"), + ) + } else { + build_print_prim_vec( + p_type.clone(), + format!("__daikon_tmp{}", first_tmp), + String::from("return"), + ) + }; + let prim_vec_record_ret = format!( + "{}\n{}\n{}", + build_tmp_vec_prim( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + String::from("__daikon_ret") + ), + build_pointer_vec_ret(), + print_vec.clone() + ); + *i = self.insert_into_block(*i, prim_vec_record_ret, body); + } + BasicType::UserDefVec(basic_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let userdef_vec_record_ret = format!( + "{}\n{}\n{}\n{}", + build_daikon_tmp_vec( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + String::from("__daikon_ret") + ), + build_pointer_vec_ret(), + build_print_pointer_vec( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + String::from("return") + ), // ? + build_print_vec_fields( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + String::from("return") + ) + ); + *i = self.insert_into_block(*i, userdef_vec_record_ret, body); + } + BasicType::PrimArray(p_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec( + format!("__daikon_tmp{}", first_tmp), + String::from("return"), + ) + } else { + build_print_prim_vec( + p_type.clone(), + format!("__daikon_tmp{}", first_tmp), + String::from("return"), + ) + }; + let prim_vec_record_ret = format!( + "{}\n{}\n{}", + build_tmp_vec_prim( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + String::from("__daikon_ret") + ), + build_pointer_arr_ret(), + print_vec.clone() + ); + *i = self.insert_into_block(*i, prim_vec_record_ret, body); + } + BasicType::UserDefArray(basic_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let userdef_vec_record_ret = format!( + "{}\n{}\n{}\n{}", + build_daikon_tmp_vec( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + String::from("__daikon_ret") + ), + build_pointer_arr_ret(), + build_print_pointer_vec( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + String::from("return") + ), + build_print_vec_fields( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + String::from("return") + ) + ); + *i = self.insert_into_block(*i, userdef_vec_record_ret, body); + } + BasicType::NoRet => {} + BasicType::Error => panic!("ret_ty is BasicType::Error"), + } + + *i = self.insert_into_block(*i, dtrace_newline(), body); + + let ret = build_ret(); + *i = self.insert_into_block(*i, ret, body); + + // remove old return stmt + body.stmts.remove(*i); + } + + // Given a block body and an index i, process the stmt + // body.stmts[i]. This may be a return stmt or a new block, + // or a stmt which invalidates one of the parameters such + // as drop(param1). + // Returns the index to the next stmt to process. If new + // stmts have been added, returns the next stmt after all + // inserted stmts. + // loc: index representing the index to the stmt to process + // body: surrounding block containing the stmt + // exit_counter: int representing the next number to use to + // label an exit ppt + // ppt_name: the program point name + // dtrace_param_blocks: Vec of String representing + // instrumentation which should be + // added at exit ppts + // param_to_block_idx: + // ret_ty: The return type of the function + #[allow(rustc::default_hash_types)] + fn grok_stmt( + &mut self, + loc: usize, + body: &mut Box, + exit_counter: &mut usize, + ppt_name: String, + dtrace_param_blocks: &mut Vec, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + daikon_tmp_counter: &mut u32, + ) -> usize { + let mut i = loc; + let stmt = body.stmts[i].clone(); + match &mut body.stmts[i].kind { + StmtKind::Let(_local) => { + return i + 1; + } + StmtKind::Item(_item) => { + return i + 1; + } + StmtKind::Expr(no_semi_expr) => match &mut no_semi_expr.kind { + // Blocks. + // recurse on nested block, + // but we still only grokked one (block) stmt, so just + // move to the next stmt (return i+1) + ExprKind::Block(block, _) => { + self.grok_block( + ppt_name.clone(), + block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + return i + 1; + } + ExprKind::If(_, if_block, None) => { + // no else + self.grok_block( + ppt_name.clone(), + if_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + return i + 1; + } + ExprKind::If(_, if_block, Some(expr)) => { + // yes else + self.grok_block( + ppt_name.clone(), + if_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + + self.grok_expr_for_if( + expr, + exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + daikon_tmp_counter, + ); + return i + 1; + } + ExprKind::While(_, while_block, _) => { + self.grok_block( + ppt_name.clone(), + while_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + return i + 1; + } + ExprKind::ForLoop { pat: _, iter: _, body: for_block, label: _, kind: _ } => { + self.grok_block( + ppt_name.clone(), + for_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + return i + 1; + } + ExprKind::Loop(loop_block, _, _) => { + self.grok_block( + ppt_name.clone(), + loop_block, + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + exit_counter, + daikon_tmp_counter, + ); + return i + 1; + } + // Not sure how to handle match blocks + ExprKind::Match(_, arms, _) => { + let mut j = 0; + while j < arms.len() { + match &mut arms[j].body { + None => {} + Some(bd) => match &mut bd.kind { + ExprKind::Block(_block, _) => { + // self.grok_block(ppt_name.clone(), + // block, + // dtrace_param_blocks, + // ¶m_to_block_idx, + // &ret_ty, + // exit_counter, + // daikon_tmp_counter); + } + _ => {} // TODO: more careful analysis on whether this is supposed to be a return expr or not, e.g. println/panic vs 7. + }, + } + j += 1; + } + return i + 1; + } // TryBlock, Const block? probably more + _ => {} + }, + _ => {} + } + // Next, look for return stmts. + // We start a new match block since we may be adding stmts + // into the block, so it does not make sense to relinquish + // mutability to the match block as above. The compiler + // will complain. + match &stmt.kind { + StmtKind::Semi(semi) => match &semi.kind { + ExprKind::Ret(None) => { + let exit = build_exit(ppt_name.clone(), *exit_counter); + *exit_counter += 1; + i = self.insert_into_block(i, exit.clone(), body); + for param_block in &mut *dtrace_param_blocks { + // DAIKON TMP ERROR: you will end up using the same __daikon_tmpX values, + // but Rust doesn't care. Not high-priority, just weird to see + // let __daikon_tmp7 = ... twice in the same scope. + i = self.insert_into_block(i, param_block.clone(), body); + } + + i = self.insert_into_block(i, dtrace_newline(), body); + + // we're sitting on the void return we just processed, so inc + // to move on + i += 1; + } + ExprKind::Ret(Some(return_expr)) => { + self.build_return( + &mut i, + &return_expr, + body, + exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ret_ty, + daikon_tmp_counter, + ); + } + ExprKind::Call(_call, _params) => { + return i + 1; + } // Maybe check for drop and other invalidations + _ => { + return i + 1; + } // other things you overlooked + }, + // Now, any stmt without a semicolon must be a trailing return? + // Blocks are no-semi exprs, but we should have caught them in the + // previous match block. + StmtKind::Expr(no_semi_expr) => { + // we know it is not a block, so it must be trailing no-semi return expr + self.build_return( + &mut i, + &no_semi_expr, + body, + exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ret_ty, + daikon_tmp_counter, + ); + } + _ => { + return i + 1; + } + } + i + } + + // Get 'impl X { }' as an Item struct. + // This will be transformed into a new impl with dtrace routines. + fn base_impl_item(&mut self) -> Box { + let base_impl = base_impl(); + let base_impl_item = self.parser.parse_items_from_string(base_impl); + match &base_impl_item { + Err(_why) => panic!("Parsing base impl failed"), + Ok(base_impl_item) => base_impl_item[0].clone(), + } + } + + // Hack: this routine is currently used to handle enums and unions. + // This will be fixed by using a /tmp file in a first pass. + fn gen_impl_noop(&mut self, pp_struct: &String, struct_generics: &Generics) { + let mut impl_item = self.base_impl_item(); + let the_impl = match &mut impl_item.kind { + ItemKind::Impl(i) => i, + _ => panic!("Base impl is not impl"), + }; + let mut stop = false; + let spliced_struct = splice_struct(&pp_struct, &mut stop); + if stop { + return; + } + + let struct_as_ret = build_phony_ret(spliced_struct.clone()); + the_impl.self_ty = match &self.parser.parse_items_from_string(struct_as_ret) { + Err(_why) => panic!("Parsing phony arg failed"), + Ok(arg_items) => match &arg_items[0].kind { + ItemKind::Fn(phony) => match &phony.sig.decl.output { + FnRetTy::Ty(ty) => ty.clone(), + _ => panic!("Phony ret is none"), + }, + _ => panic!("Parsing phony fn failed"), + }, + }; + the_impl.generics = struct_generics.clone(); + + // We only need dtrace_print_fields and dtrace_print_fields_vec. xfield routines are only internal. + let dtrace_print_fields_fn_noop = self.build_dtrace_print_fields_noop(); + match &self.parser.parse_items_from_string(dtrace_print_fields_fn_noop) { + Err(_) => panic!("Parsing dtrace_print_fields_noop failed"), + Ok(items) => match &items[0].kind { + ItemKind::Impl(tmp_impl) => { + the_impl.items.push(tmp_impl.items[0].clone()); + } + _ => panic!("Expected impl for noop 1"), + }, + } + + let plain_struct = cut_lifetimes(spliced_struct.clone()); + let dtrace_print_fields_vec = self.build_dtrace_print_fields_vec_noop(plain_struct.clone()); + match &self.parser.parse_items_from_string(dtrace_print_fields_vec) { + Err(_) => panic!("Parsing dtrace_print_fields_vec failed"), + Ok(items) => match &items[0].kind { + ItemKind::Impl(tmp_impl) => { + the_impl.items.push(tmp_impl.items[0].clone()); + } + _ => panic!("Expected phony impl 2"), + }, + } + + self.mod_items.push(impl_item.clone()); + } + + // This function generates a new impl for a user-defined struct with type + // ty. The impl will contain multiple synthesized functions, like + // dtrace_print_fields, dtrace_print_fields_vec, and more. + fn gen_impl(&mut self, fields: &mut ThinVec, ty: &Ty, struct_generics: &Generics) { + let mut impl_item = self.base_impl_item(); + let the_impl = match &mut impl_item.kind { + ItemKind::Impl(i) => i, + _ => panic!("Base impl is not impl"), + }; + // let spliced_struct = splice_struct(&pp_struct); + // let struct_as_ret = build_phony_ret(spliced_struct.clone()); // TODO: fix splice string to handle pub keyword + the_impl.self_ty = Box::new(ty.clone()); + // match &self.parser.parse_items_from_string(struct_as_ret) { + // Err(_why) => panic!("Parsing phony arg failed"), + // Ok(arg_items) => match &arg_items[0].kind { + // ItemKind::Fn(phony) => match &phony.sig.decl.output { + // FnRetTy::Ty(ty) => ty.clone(), + // _ => panic!("Phony ret is none") + // } + // _ => panic!("Parsing phony fn failed") + // } + // }; + the_impl.generics = struct_generics.clone(); + + let dtrace_print_fields_fn = self.build_dtrace_print_fields(fields); + match &self.parser.parse_items_from_string(dtrace_print_fields_fn) { + Err(_why) => panic!("Parsing dtrace_print_fields failed"), + Ok(items) => match &items[0].kind { + ItemKind::Impl(tmp_impl) => { + the_impl.items.push(tmp_impl.items[0].clone()); + } + _ => panic!("Expected phony impl 1"), + }, + } + + // Is this even important? + // let plain_struct = cut_lifetimes(spliced_struct.clone()); + let plain_struct = match &ty.kind { + TyKind::Path(_, path) => String::from(path.segments[0].ident.as_str()), + _ => panic!("Why don't we have a path?"), + }; + let dtrace_print_fields_vec = + self.build_dtrace_print_fields_vec(plain_struct.clone(), fields); + match &self.parser.parse_items_from_string(dtrace_print_fields_vec) { + Err(_) => panic!("Parsing dtrace_print_fields_vec failed"), + Ok(items) => match &items[0].kind { + ItemKind::Impl(tmp_impl) => { + the_impl.items.push(tmp_impl.items[0].clone()); + } + _ => panic!("Expected phony impl 2"), + }, + } + + // build dtrace_print_xfield_vec (AND dtrace_print_xfield...) here, then that should be it for generating fns in the impl. + let dtrace_print_xfields = self.build_dtrace_print_xfield_vec(plain_struct.clone(), fields); + match &self.parser.parse_items_from_string(dtrace_print_xfields) { + Err(_) => panic!("Parsing dtrace_print_xfields failed"), + Ok(items) => match &items[0].kind { + ItemKind::Impl(tmp_impl) => { + let mut i = 0; + while i < tmp_impl.items.len() { + the_impl.items.push(tmp_impl.items[i].clone()); + i += 1; + } + } + _ => panic!("Expected phony impl 3"), + }, + } + + self.mod_items.push(impl_item.clone()); + } + + // Given a struct fields, returns a String with code containing + // functions to log each field of the struct given a vec of + // such a struct. + // Additionally, for any Vec or array fields, adds a function + // which is responsible for logging these fields. + fn build_dtrace_print_xfield_vec( + &mut self, + plain_struct: String, + fields: &ThinVec, + ) -> String { + // WARNING: also building dtrace_print_xfield here... be careful about different issues. + // dtrace_print_xfield_vec prints scalar fields out of a Vec, and dtrace_print_xfield takes one Me and prints Me.f which is a Vec. + let mut dtrace_print_xfields_vec = dtrace_print_xfields_vec_prologue(); + + // not important for this to be here, these functions are self-contained so + // the names don't matter. + let mut daikon_tmp_counter = 0; + let mut i = 0; + while i < fields.len() { + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + + let mut is_ref = false; + let dtrace_print_xfield = match &get_basic_type(&fields[i].ty.kind, &mut is_ref) { + BasicType::Prim(p_type) => { + // We have a vec of ourselves, and the field is + if p_type == "String" || p_type == "str" { + build_print_xfield_string(field_name.clone(), plain_struct.clone()) + } else { + build_print_xfield(field_name.clone(), plain_struct.clone()) // TODO: change this name to involve vec to be clear. + } + } + // TODO: mash: + // build_dtrace_print_xfield_prologue(), + // build_tmp_prim_vec_for_field(), + // build_pointer_vec_userdef(), + // build_dtrace_print_xfield_middle(), + // build_print_prim_vec_for_field(), + // build_dtrace_print_xfield_epilogue() + BasicType::PrimVec(p_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec_for_field( + format!("__daikon_tmp{}", first_tmp), + field_name.clone(), + ) + } else { + build_print_prim_vec_for_field( + p_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone(), + ) + }; + let f1 = format!( + "{}\n{}\n{}\n{}\n{}\n{}", + build_dtrace_print_xfield_prologue(field_name.clone()), + build_tmp_prim_vec_for_field( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + field_name.clone() + ), + build_pointer_vec_userdef(field_name.clone()), + build_dtrace_print_xfield_middle(), + print_vec.clone(), + build_dtrace_print_xfield_epilogue() + ); + let f2 = + build_print_xfield_for_vec(field_name.clone(), plain_struct.to_string()); + format!("{}\n{}", f1, f2) + } + // TODO: mash: + // build_dtrace_print_xfield_prologue(), + // build_tmp_vec_for_field(), + // build_pointer_vec_userdef() (for single pointer), + // build_pointers_vec_userdef() (for pointers), + // build_dtrace_print_xfield_middle() (depth check), + // build_print_vec_fields_for_field() (for contents), + // build_dtrace_print_xfield_epilogue() (closing brace) + BasicType::UserDefVec(basic_struct) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + // We maintain that is_ref represents Vec/array args in this case. + let tmp_vec = if is_ref { + build_tmp_vec_for_field( + first_tmp.clone(), + basic_struct.to_string(), + next_tmp.clone(), + field_name.clone(), + ) + } else { + build_tmp_vec_for_field_ampersand( + first_tmp.clone(), + basic_struct.to_string(), + next_tmp.clone(), + field_name.clone(), + ) + }; + let f1 = format!( + "{}\n{}\n{}\n{}\n{}\n{}\n{}", + build_dtrace_print_xfield_prologue(field_name.clone()), + tmp_vec.clone(), + build_pointer_vec_userdef(field_name.clone()), + build_pointers_vec_userdef( + basic_struct.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ), + build_dtrace_print_xfield_middle(), + build_print_vec_fields_for_field( + basic_struct.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ), + build_dtrace_print_xfield_epilogue() + ); + let f2 = build_print_xfield_for_vec(field_name.clone(), plain_struct.clone()); + format!("{}\n{}", f1, f2) + } + // TODO: arrays, mighty similar to vec. Maybe you can cheat and just do the exact same thing... use | in pattern matching. + // Except pointer is diff, as_ptr() as usize vs as *const _ as *const () as usize... + BasicType::PrimArray(p_type) => { + // UNTRUSTED: + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec_for_field( + format!("__daikon_tmp{}", first_tmp), + field_name.clone(), + ) + } else { + build_print_prim_vec_for_field( + p_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone(), + ) + }; + let f1 = format!( + "{}\n{}\n{}\n{}\n{}\n{}", + build_dtrace_print_xfield_prologue(field_name.clone()), + build_tmp_prim_vec_for_field( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + field_name.clone() + ), + build_pointer_arr_userdef(field_name.clone()), + build_dtrace_print_xfield_middle(), + print_vec.clone(), + build_dtrace_print_xfield_epilogue() + ); + let f2 = + build_print_xfield_for_vec(field_name.clone(), plain_struct.to_string()); + format!("{}\n{}", f1, f2) + } + BasicType::UserDefArray(basic_struct) => { + // UNTRUSTED: + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + // We maintain that is_ref represents Vec/array args in this case. + let tmp_vec = if is_ref { + build_tmp_vec_for_field( + first_tmp.clone(), + basic_struct.to_string(), + next_tmp.clone(), + field_name.clone(), + ) + } else { + build_tmp_vec_for_field_ampersand( + first_tmp.clone(), + basic_struct.to_string(), + next_tmp.clone(), + field_name.clone(), + ) + }; + let f1 = format!( + "{}\n{}\n{}\n{}\n{}\n{}\n{}", + build_dtrace_print_xfield_prologue(field_name.clone()), + tmp_vec.clone(), + build_pointer_arr_userdef(field_name.clone()), + build_pointers_vec_userdef( + basic_struct.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ), + build_dtrace_print_xfield_middle(), + build_print_vec_fields_for_field( + basic_struct.to_string(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ), + build_dtrace_print_xfield_epilogue() + ); + let f2 = build_print_xfield_for_vec(field_name.clone(), plain_struct.clone()); + format!("{}\n{}", f1, f2) + } + _ => String::from(""), + }; + + dtrace_print_xfields_vec.push_str(&dtrace_print_xfield); + i += 1; + } + let res = format!("{}{}", dtrace_print_xfields_vec, dtrace_print_xfields_vec_epilogue()); + res + } + + // Just stuff the plain_struct in there. + fn build_dtrace_print_fields_vec_noop(&mut self, plain_struct: String) -> String { + format!("impl __skip {{ {} }}", build_dtrace_print_fields_vec_noop(plain_struct)) + } + + // Builds the toplevel function which is called to log a Vec or array of a given struct. + fn build_dtrace_print_fields_vec( + &mut self, + plain_struct: String, + fields: &ThinVec, + ) -> String { + let mut dtrace_print_fields_vec = dtrace_print_fields_vec_prologue(plain_struct.clone()); + + let mut daikon_tmp_counter = 0; + let mut i = 0; + while i < fields.len() { + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + + let mut is_ref = false; + let dtrace_field_vec_rec = match &get_basic_type(&fields[i].ty.kind, &mut is_ref) { + // don't need p_type because we just call dtrace_print_xfield which handles the type. + BasicType::Prim(_) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + format!( + "{}\n{}", + build_daikon_tmp_vec_userdef( + first_tmp.clone(), + plain_struct.clone(), + next_tmp + ), + build_print_xfield_vec( + plain_struct.clone(), + field_name.clone(), + format!("__daikon_tmp{}", first_tmp) + ) + ) + } + BasicType::UserDef(field_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let tmp_vec = if !is_ref { + build_daikon_tmp_vec_field_userdef_ampersand( + first_tmp.clone(), + field_type.clone(), + next_tmp.clone(), + field_name.clone(), + ) + } else { + build_daikon_tmp_vec_field_userdef( + first_tmp.clone(), + field_type.clone(), + next_tmp.clone(), + field_name.clone(), + ) + }; + format!( + "{}\n{}\n{}", + tmp_vec, + build_print_pointer_vec_userdef( + field_type.clone(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ), + build_print_vec_fields_userdef( + field_type.clone(), + format!("__daikon_tmp{}", first_tmp), + field_name.clone() + ) + ) + } + // call X::dtrace_print__vec since it will be implemented to only print pointers. NOT TRUSTED CODE: + BasicType::PrimVec(_) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + format!( + "{}\n{}", + build_daikon_tmp_vec_userdef( + first_tmp.clone(), + plain_struct.clone(), + next_tmp + ), + build_print_xfield_vec( + plain_struct.clone(), + field_name.clone(), + format!("__daikon_tmp{}", first_tmp) + ) + ) + } + BasicType::UserDefVec(_) => { + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + format!( + "{}\n{}", + build_daikon_tmp_vec_userdef( + first_tmp.clone(), + plain_struct.clone(), + next_tmp + ), + build_print_xfield_vec( + plain_struct.clone(), + field_name.clone(), + format!("__daikon_tmp{}", first_tmp) + ) + ) + } + BasicType::PrimArray(_) => { + // UNTRUSTED: is this exactly the same? + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + format!( + "{}\n{}", + build_daikon_tmp_vec_userdef( + first_tmp.clone(), + plain_struct.clone(), + next_tmp + ), + build_print_xfield_vec( + plain_struct.clone(), + field_name.clone(), + format!("__daikon_tmp{}", first_tmp) + ) + ) + } + BasicType::UserDefArray(_) => { + // UNTRUSTED: is this exactly the same? + let first_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + daikon_tmp_counter += 1; + format!( + "{}\n{}", + build_daikon_tmp_vec_userdef( + first_tmp.clone(), + plain_struct.clone(), + next_tmp + ), + build_print_xfield_vec( + plain_struct.clone(), + field_name.clone(), + format!("__daikon_tmp{}", first_tmp) + ) + ) + } + BasicType::NoRet => String::from(""), + BasicType::Error => panic!("Field type not handled"), + }; + + dtrace_print_fields_vec.push_str(&dtrace_field_vec_rec); // don't think a newline here would matter + i += 1; + } + + let res = format!("{}{}", dtrace_print_fields_vec, dtrace_print_fields_vec_epilogue()); + res + } + + fn build_dtrace_print_fields_noop(&mut self) -> String { + format!("impl __skip {{ {} }}", build_dtrace_print_fields_noop()) + } + + // Given a struct's field declarations, generate the function dtrace_print_fields(self) + // to be added to the synthesized impl block. + fn build_dtrace_print_fields(&mut self, fields: &mut ThinVec) -> String { + let mut dtrace_print_fields: String = dtrace_print_fields_prologue(); + + let mut i = 0; + while i < fields.len() { + // Make all fields public for access in dtrace routines + fields[i].vis.kind = VisibilityKind::Public; + + let field_name = match &fields[i].ident { + Some(field_ident) => String::from(field_ident.as_str()), + None => panic!("Field has no identifier"), + }; + + let mut is_ref = false; + let mut dtrace_field_rec = match &get_basic_type(&fields[i].ty.kind, &mut is_ref) { + BasicType::Prim(p_type) => { + if p_type == "String" || p_type == "str" { + build_prim_field_tostring(field_name.clone()) + } else if is_ref { + build_field_prim_ref(p_type.clone(), field_name.clone()) + } else { + build_field_prim(p_type.clone(), field_name.clone()) + } + } + BasicType::UserDef(_) => { + if !is_ref { + build_field_userdef_with_ampersand_access(field_name.clone()) + } else { + build_field_userdef(field_name.clone()) + } + } + // TODO: use the | here. + BasicType::PrimVec(_) => build_call_print_field(field_name.clone()), + BasicType::UserDefVec(_) => build_call_print_field(field_name.clone()), + BasicType::PrimArray(_p_type) => { + // UNTRUSTED: + build_call_print_field(field_name.clone()) + } + BasicType::UserDefArray(_) => { + // UNTRUSTED: + build_call_print_field(field_name.clone()) + } + BasicType::NoRet => String::from(""), + BasicType::Error => panic!("Field type not handled"), + }; + dtrace_field_rec.push_str("\n"); + + dtrace_print_fields.push_str(&format!("{}{}", dtrace_field_rec, "\n")); + i += 1; + } + + format!("{}{}", dtrace_print_fields, dtrace_print_fields_epilogue()) + } + + // TODO: dtrace calls should be represented with a better data structures rather than + // Strings + // Given a function signature, generate a set of dtrace calls for each parameter. These + // will be reused at the function entry and each exit ppt. + fn grok_fn_sig(&mut self, decl: &Box, daikon_tmp_counter: &mut u32) -> Vec { + // grok params + let mut i = 0; + let mut dtrace_param_blocks: Vec = Vec::new(); + while i < decl.inputs.len() { + let mut is_ref = false; + let mut dtrace_rec = if get_param_ident(&decl.inputs[i].pat) == "self" { + build_userdef(get_param_ident(&decl.inputs[i].pat), 3 /* depth_arg */) + } else { + match &get_basic_type(&decl.inputs[i].ty.kind, &mut is_ref) { + BasicType::Prim(p_type) => { + if p_type == "String" || p_type == "str" { + build_prim_with_tostring(get_param_ident(&decl.inputs[i].pat)) + } else if is_ref { + build_prim_ref(p_type.clone(), get_param_ident(&decl.inputs[i].pat)) + } else { + build_prim(p_type.clone(), get_param_ident(&decl.inputs[i].pat)) + } + } + BasicType::UserDef(_) => { + if !is_ref { + build_userdef_with_ampersand_access( + get_param_ident(&decl.inputs[i].pat), + 3, /* depth_arg */ + ) + } else { + build_userdef( + get_param_ident(&decl.inputs[i].pat), + 3, /* depth_arg */ + ) + } + } + BasicType::PrimVec(p_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec( + format!("__daikon_tmp{}", first_tmp), + get_param_ident(&decl.inputs[i].pat), + ) + } else { + build_print_prim_vec( + p_type.clone(), + format!("__daikon_tmp{}", first_tmp), + get_param_ident(&decl.inputs[i].pat), + ) + }; + format!( + "{}\n{}\n{}", + build_tmp_vec_prim( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + get_param_ident(&decl.inputs[i].pat) + ), + build_pointer_vec(get_param_ident(&decl.inputs[i].pat)), + print_vec.clone() + ) + } + BasicType::UserDefVec(basic_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let var_name = get_param_ident(&decl.inputs[i].pat); + // We maintain that is_ref represents Vec/array argument in this case. + let tmp_vec = if is_ref { + build_daikon_tmp_vec( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + var_name.clone(), + ) + } else { + build_daikon_tmp_vec_ampersand( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + var_name.clone(), + ) + }; + let res = format!( + "{}\n{}\n{}\n{}", + tmp_vec.clone(), + build_pointer_vec(var_name.clone()), + build_print_pointer_vec( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + var_name.clone() + ), + build_print_vec_fields( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + var_name.clone() + ) + ); + res + } + BasicType::PrimArray(p_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let print_vec = if p_type == "String" || p_type == "str" { + build_print_string_vec( + format!("__daikon_tmp{}", first_tmp), + get_param_ident(&decl.inputs[i].pat), + ) + } else { + build_print_prim_vec( + p_type.clone(), + format!("__daikon_tmp{}", first_tmp), + get_param_ident(&decl.inputs[i].pat), + ) + }; + format!( + "{}\n{}\n{}", + build_tmp_vec_prim( + first_tmp.clone(), + p_type.to_string(), + next_tmp.clone(), + get_param_ident(&decl.inputs[i].pat) + ), + build_pointer_arr(get_param_ident(&decl.inputs[i].pat)), + print_vec.clone() + ) + } + BasicType::UserDefArray(basic_type) => { + let first_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let next_tmp = daikon_tmp_counter.to_string(); + *daikon_tmp_counter += 1; + let var_name = get_param_ident(&decl.inputs[i].pat); + // We maintain that is_ref represents Vec/array argument in this case. + let tmp_vec = if is_ref { + build_daikon_tmp_vec( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + var_name.clone(), + ) + } else { + build_daikon_tmp_vec_ampersand( + first_tmp.clone(), + basic_type.to_string(), + next_tmp.clone(), + var_name.clone(), + ) + }; + let res = format!( + "{}\n{}\n{}\n{}", + tmp_vec.clone(), + build_pointer_arr(var_name.clone()), + build_print_pointer_vec( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + var_name.clone() + ), + build_print_vec_fields( + basic_type.to_string(), + format!("__daikon_tmp{}", first_tmp), + var_name.clone() + ) + ); + res + } + BasicType::NoRet => String::from(""), + BasicType::Error => panic!("Formal arg type not handled."), + } + }; + dtrace_rec.push_str("\n"); + + dtrace_param_blocks.push(format!("{}{}", dtrace_rec, "\n")); + i += 1; + } + + // Return param-dependent dtrace calls + dtrace_param_blocks + } + + // Walk a single block, used for recursing through nested + // blocks like if stmts and loops. + // ppt_name: program point name + // body: the block which we will walk + // dtrace_param_blocks: Vec of Strings containing dtrace + // calls for each parameter. + // param_to_block_idx: + // ret_ty: Function return type + // exit_counter: contains the next label for an exit ppt + // daikon_tmp_counter: contains the next label for a + // temporary variable + #[allow(rustc::default_hash_types)] + fn grok_block( + &mut self, + ppt_name: String, + body: &mut Box, + dtrace_param_blocks: &mut Vec, + param_to_block_idx: &HashMap, + ret_ty: &FnRetTy, + exit_counter: &mut usize, + daikon_tmp_counter: &mut u32, + ) { + let mut i = 0; + + // Assuming no unreachable statements. + while i < body.stmts.len() { + i = self.grok_stmt( + i, + body, + exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + daikon_tmp_counter, + ); + } + } + + // Walk the function body and insert dtrace calls at + // the beginning and at exit points. + #[allow(rustc::default_hash_types)] + fn grok_fn_body( + &mut self, + ppt_name: String, + body: &mut Box, + dtrace_param_blocks: &mut Vec, + param_to_block_idx: HashMap, + ret_ty: &FnRetTy, + daikon_tmp_counter: &mut u32, + ) { + let mut i = 0; + + // How nonces should be done-- + // lock a global counter shared by all threads + // store its current value + // increment it + // unlock + // use the stored value at all exit points in this function + // Currently there is a nonce counter per file which is not right. + i = self.insert_into_block(i, init_nonce(), body); + + let entry = build_entry(ppt_name.clone()); + i = self.insert_into_block(i, entry, body); + for param_block in &mut *dtrace_param_blocks { + i = self.insert_into_block(i, param_block.clone(), body); + } + i = self.insert_into_block(i, dtrace_newline(), body); + + // Before grokking fn body, turn implicit void return into "return;" + // this may be unreachable in some situations like + // fn foo(t: bool) { if t { return; } else { return; } } + // In this situation we should not add a return stmt, but + // I cannot detect this yet, maybe there is a better solution + // to detecting the end of a function that returns void. + match &ret_ty { + FnRetTy::Default(_) => { + if body.stmts.len() == 0 || !last_stmt_is_void_return(body) { + self.append_to_block(build_void_return(), body); + } + } + _ => {} + } + + let mut exit_counter = 1; + + // Assuming no unreachable statements. + while i < body.stmts.len() { + i = self.grok_stmt( + i, + body, + &mut exit_counter, + ppt_name.clone(), + dtrace_param_blocks, + ¶m_to_block_idx, + &ret_ty, + daikon_tmp_counter, + ) + } + } +} + +// The main visitor routines and entry-points for function and struct +// instrumentation. +impl<'a> MutVisitor for DaikonDtraceVisitor<'a> { + // Process the function signature to generate calls to log runtime values. + // Walk the function body and insert calls at exit points. + fn visit_fn(&mut self, mut fk: FnKind<'_>, _span: rustc_span::Span, _id: rustc_ast::NodeId) { + match &mut fk { + FnKind::Fn(_, _, f) => { + let ppt_name = String::from(f.ident.as_str()); + if ppt_name == "execute" { + return; + } + let mut daikon_tmp_counter = 0; + // get block of dtrace chunks -- one for each param (in a String, not good) + let mut dtrace_param_blocks = + self.grok_fn_sig(&f.sig.decl, &mut daikon_tmp_counter); + let param_to_block_idx = map_params(&f.sig.decl); + match &mut f.body { + None => {} + Some(body) => { + self.grok_fn_body( + ppt_name.clone(), + body, + &mut dtrace_param_blocks, + param_to_block_idx, + &f.sig.decl.output, + &mut daikon_tmp_counter, + ); + } + } + } + _ => {} + } + mut_visit::walk_fn(self, fk); + } + + // Visit all structs and generate new impl blocks with dtrace + // routine definitions + // TODO: look up struct names in a /tmp file to determine + // whether to continue or not. + fn visit_item(&mut self, item: &mut Item) { + let get_struct = pprust::item_to_string(&item); + match &mut item.kind { + ItemKind::Enum(_ident, generics, _enum_def) => { + // TODO: remove + self.gen_impl_noop(&get_struct, &generics); + } + ItemKind::Struct(ident, generics, variant_data) => match variant_data { + VariantData::Struct { fields, recovered: _recovered } => { + let mut the_path = Path::from_ident(ident.clone()); + let mut the_args: ThinVec = ThinVec::new(); + let mut i = 0; + while i < generics.params.len() { + match &generics.params[i].kind { + GenericParamKind::Lifetime => { + the_args.push(AngleBracketedArg::Arg(GenericArg::Lifetime( + Lifetime { + id: NodeId::MAX_AS_U32.into(), + ident: generics.params[i].ident.clone(), + }, + ))); + } + GenericParamKind::Type { default: _ } => { + panic!("Enum has type generic arg.") + } + GenericParamKind::Const { ty: _, span: _, default: _ } => { + panic!("Enum has const generic arg.") + } + } + // Return param-dependent dtrace calls + i += 1; + } + let angle_bracketed_args = + AngleBracketedArgs { span: item.span.clone(), args: the_args }; + the_path.segments[0].args = + Some(Box::new(GenericArgs::AngleBracketed(angle_bracketed_args))); + let the_ty = Ty { + id: NodeId::MAX_AS_U32.into(), + kind: TyKind::Path(None, the_path.clone()), + span: item.span.clone(), + tokens: None, + }; + self.gen_impl(fields, &the_ty, &generics); + } + VariantData::Tuple(_, _) => {} + _ => {} + }, + ItemKind::Union(_ident, generics, _variant_data) => { + // TODO: remove + self.gen_impl_noop(&get_struct, &generics); + } + _ => {} + } + + mut_visit::walk_item(self, item); + } +} + +// --------------------- + impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> { @@ -48,6 +1890,34 @@ impl<'a> Parser<'a> { Ok(ItemKind::Mod(safety, ident, mod_kind)) } + // Convert String to items. We create a new file dtrace_parserX each time we want to + // parse some new items vec. Diagnostics sometimes point to these files for unknown + // reasons. + pub fn parse_items_from_string(&self, str: String) -> PResult<'a, ThinVec>> { + let count = *PARSER_COUNTER.lock().unwrap(); + let mut tmp_parser = unwrap_or_emit_fatal(new_parser_from_source_str( + &self.psess, + rustc_span::FileName::Custom(format!("{}{}", "dtrace_parser", count.to_string())), + str, + StripTokens::Nothing, + )); + + *PARSER_COUNTER.lock().unwrap() += 1; + + let mut tmp_items: ThinVec> = ThinVec::new(); + + // Parse from str + loop { + while tmp_parser.maybe_consume_incorrect_semicolon(tmp_items.last().map(|x| &**x)) {} + let Some(item) = tmp_parser.parse_item(ForceCollect::No)? else { + break; + }; + tmp_items.push(item); + } + + Ok(tmp_items) + } + /// Parses the contents of a module (inner attributes followed by module items). /// We exit once we hit `term` which can be either /// - EOF (for files) @@ -59,6 +1929,23 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_inner_attributes()?; + // Determine whether we are building crate std + let source_map = self.psess.source_map(); + let (source_file, _b, _c, _d, _e) = source_map.span_to_location_info(self.token.span); + *DO_VISITOR.lock().unwrap() = match &source_file { + Some(sf) => match &sf.name { + rustc_span::FileName::Real(file_name) => match &file_name { + rustc_span::RealFileName::LocalPath(path) => match &path.to_str() { + Some(str) => !str.starts_with("library") && !str.contains(".cargo"), + None => false, + }, + _ => false, + }, + _ => false, + }, + None => false, + }; + let post_attr_lo = self.token.span; let mut items: ThinVec> = ThinVec::new(); @@ -108,6 +1995,57 @@ impl<'a> Parser<'a> { } } + //== Daikon dtrace instrumentation passes ==// + + if *DO_VISITOR.lock().unwrap() { + // do all instrumentation + let mut items_to_append: ThinVec> = ThinVec::new(); + let mut impl_inserter = + DaikonDtraceVisitor { parser: &self, mod_items: &mut items_to_append }; + mut_visit::visit_items(&mut impl_inserter, &mut items); + + // push impl blocks + let mut i = 0; + while i < items_to_append.len() { + items.push(items_to_append[i].clone()); + i += 1; + } + + // pretty print the instrumented code (without library/imports) for testing + let pp_path = format!("{}{}", *OUTPUT_NAME.lock().unwrap(), ".pp"); + let pp_as_path = std::path::Path::new(&pp_path); + std::fs::File::create(&pp_as_path).unwrap(); + let mut pp = + std::fs::File::options().write(true).append(true).open(&pp_as_path).unwrap(); + i = 0; + while i < items.len() - 1 { + writeln!(&mut pp, "{}\n", pprust::item_to_string(&items[i])).ok(); + i += 1; + } + writeln!(&mut pp, "{}", pprust::item_to_string(&items[i])).ok(); + + // add imports + // TODO: you should check if these imports are already included. + match &self.parse_items_from_string(build_imports()) { + Err(_why) => panic!("Can't parse imports"), + Ok(prepend_items) => { + for item in prepend_items { + items.insert(0, item.clone()); + } + } + } + + // add daikon library + match &self.parse_items_from_string(daikon_lib()) { + Err(_why) => panic!("Can't parse daikon lib"), + Ok(lib_items) => { + for item in lib_items { + items.push(item.clone()); + } + } + } + } + let inject_use_span = post_attr_lo.data().with_hi(post_attr_lo.lo()); let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span }; Ok((attrs, items, mod_spans)) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ed4069dae933c..975f68ef6a3ea 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -3,7 +3,7 @@ mod attr_wrapper; mod diagnostics; mod expr; mod generics; -mod item; +pub mod item; mod nonterminal; mod pat; mod path; @@ -11,6 +11,8 @@ mod stmt; pub mod token_type; mod ty; +pub mod daikon_strs; + // Parsers for non-functionlike builtin macros are defined in rustc_parse so they can be used by // both rustc_builtin_macros and rustfmt. pub mod asm; diff --git a/daikon-tests/Cargo.toml b/daikon-tests/Cargo.toml new file mode 100644 index 0000000000000..e71a7e1c74220 --- /dev/null +++ b/daikon-tests/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "daikon-tests" +version = "0.1.0" +edition = "2024" + +[dependencies] +colored = "3.0.0" diff --git a/daikon-tests/src/main.rs b/daikon-tests/src/main.rs new file mode 100644 index 0000000000000..6ed84d4fb49ce --- /dev/null +++ b/daikon-tests/src/main.rs @@ -0,0 +1,85 @@ +use colored::Colorize; + +pub fn get_output_name(s: String) -> String { + let end = + match s.rfind(".") { // .rs + None => panic!("no . at the end of input file name"), + Some(end) => end + }; + let mut start = + match s.rfind("/") { // .../.rs + None => 0, + Some(slash) => slash+1 + }; + let mut res = String::from(""); + while start < end { + res.push_str(&format!("{}", s.chars().nth(start).unwrap())); + start += 1; + } + return res; +} + +fn run_daikon_rustc_pp_tests() { + // iterate through "./tests" and run rustc +daikon for files, cargo +daikon for multi-file tests in subdirectories + let test_path = std::fs::canonicalize(std::path::Path::new("./test")).unwrap(); + + // how to make each one of these a test w/o a new function? + for entry in std::fs::read_dir(test_path.clone()).unwrap() { + let entry = entry.unwrap(); + let path = std::fs::canonicalize(entry.path()).unwrap(); + if path.is_dir() { + // set current_dir to canonicalize() in Command and do cargo +daikon build + } else { + // set current_dir to canonicalize(test_path.clone()) and execute rustc +daikon + + let path_str = path.to_str().unwrap(); + if !path_str.ends_with("rs") { + continue; + } else { + let output_name = get_output_name(String::from(path_str)); + println!("Running test {}", output_name); + + std::process::Command::new("rustc") + .arg("+daikon") + .arg(path_str) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .current_dir(&test_path) + .status() + .expect("failed to execute daikon-rustc"); + + // read expected/actual pp to String + let pp_path = format!("./test/{}{}", output_name, ".pp"); + let pp_as_path = std::path::Path::new(&pp_path); + let pp_as_path_buf = std::fs::canonicalize(pp_as_path).unwrap(); + let actual = std::fs::read_to_string(&pp_as_path_buf).unwrap(); + let pp_expected_path = format!("./test/{}-expected{}", output_name, ".pp"); + let pp_expected_as_path = std::path::Path::new(&pp_expected_path); + let pp_expected_as_path_buf = std::fs::canonicalize(pp_expected_as_path).unwrap(); + let expected = std::fs::read_to_string(&pp_expected_as_path_buf).unwrap(); + + // remove junk + let exec_path = format!("./test/{}", output_name); + std::fs::remove_file(std::path::Path::new(&exec_path)).unwrap(); + let decls_path = format!("./test/{}.decls", output_name); + std::fs::remove_file(std::path::Path::new(&decls_path)).unwrap(); + let dtrace_path = format!("./test/{}.dtrace", output_name); + std::fs::remove_file(std::path::Path::new(&dtrace_path)).unwrap(); + let pp_path = format!("./test/{}.pp", output_name); + std::fs::remove_file(std::path::Path::new(&pp_path)).unwrap(); + + // check + assert_eq!(expected, actual); + println!("{}", "Pass".green()); + } + } + } + + println!("\n{}", "All tests passed".green()); + +} + +fn main() { + run_daikon_rustc_pp_tests(); +} diff --git a/daikon-tests/test/1-expected.pp b/daikon-tests/test/1-expected.pp new file mode 100644 index 0000000000000..2709366e12265 --- /dev/null +++ b/daikon-tests/test/1-expected.pp @@ -0,0 +1,12 @@ +fn main() { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("main:::ENTER", __daikon_nonce); + dtrace_newline(); + dtrace_exit("main:::EXIT1", __daikon_nonce); + dtrace_newline(); + return; +} diff --git a/daikon-tests/test/1-impl-expected.pp b/daikon-tests/test/1-impl-expected.pp new file mode 100644 index 0000000000000..9c649c10d27f8 --- /dev/null +++ b/daikon-tests/test/1-impl-expected.pp @@ -0,0 +1,23 @@ +struct X {} + +fn main() { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("main:::ENTER", __daikon_nonce); + dtrace_newline(); + dtrace_exit("main:::EXIT1", __daikon_nonce); + dtrace_newline(); + return; +} + +impl X<> { + pub fn dtrace_print_fields(&self, depth: i32, prefix: String) { + if depth == 0 { return; } + } + pub fn dtrace_print_fields_vec(v: &Vec<&X>, depth: i32, prefix: String) { + if depth == 0 { return; } + } +} diff --git a/daikon-tests/test/1-impl.rs b/daikon-tests/test/1-impl.rs new file mode 100644 index 0000000000000..58d79d7558d0e --- /dev/null +++ b/daikon-tests/test/1-impl.rs @@ -0,0 +1,3 @@ +struct X {} + +fn main() {} diff --git a/daikon-tests/test/1.rs b/daikon-tests/test/1.rs new file mode 100644 index 0000000000000..e71fdf55421d0 --- /dev/null +++ b/daikon-tests/test/1.rs @@ -0,0 +1 @@ +fn main() {} \ No newline at end of file diff --git a/daikon-tests/test/no-semi-if-else-expected.pp b/daikon-tests/test/no-semi-if-else-expected.pp new file mode 100644 index 0000000000000..dd7f1b835a632 --- /dev/null +++ b/daikon-tests/test/no-semi-if-else-expected.pp @@ -0,0 +1,38 @@ +fn test(x: i32) -> i32 { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("test:::ENTER", __daikon_nonce); + dtrace_print_prim::(x, String::from("x")); + dtrace_newline(); + if x % 2 == 0 { + dtrace_exit("test:::EXIT1", __daikon_nonce); + dtrace_print_prim::(x, String::from("x")); + let __daikon_ret: i32 = 1; + dtrace_print_prim::(__daikon_ret, String::from("return")); + dtrace_newline(); + return __daikon_ret; + } else { + dtrace_exit("test:::EXIT2", __daikon_nonce); + dtrace_print_prim::(x, String::from("x")); + let __daikon_ret: i32 = 2; + dtrace_print_prim::(__daikon_ret, String::from("return")); + dtrace_newline(); + return __daikon_ret; + } +} + +fn main() { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("main:::ENTER", __daikon_nonce); + dtrace_newline(); + dtrace_exit("main:::EXIT1", __daikon_nonce); + dtrace_newline(); + return; +} diff --git a/daikon-tests/test/no-semi-if-else.rs b/daikon-tests/test/no-semi-if-else.rs new file mode 100644 index 0000000000000..d74003d6fb161 --- /dev/null +++ b/daikon-tests/test/no-semi-if-else.rs @@ -0,0 +1,9 @@ +fn test(x: i32) -> i32 { + if x % 2 == 0 { + 1 + } else { + 2 + } +} + +fn main() {} diff --git a/daikon-tests/test/non-ret-if-else-expected.pp b/daikon-tests/test/non-ret-if-else-expected.pp new file mode 100644 index 0000000000000..e3d65e38121ca --- /dev/null +++ b/daikon-tests/test/non-ret-if-else-expected.pp @@ -0,0 +1,30 @@ +fn boop(x: i32) -> i32 { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("boop:::ENTER", __daikon_nonce); + dtrace_print_prim::(x, String::from("x")); + dtrace_newline(); + let y = if x == 12 { 22 } else { 19 }; + dtrace_exit("boop:::EXIT1", __daikon_nonce); + dtrace_print_prim::(x, String::from("x")); + let __daikon_ret: i32 = y; + dtrace_print_prim::(__daikon_ret, String::from("return")); + dtrace_newline(); + return __daikon_ret; +} + +fn main() { + let mut __daikon_nonce = 0; + let mut __unwrap_nonce = NONCE_COUNTER.lock().unwrap(); + __daikon_nonce = *__unwrap_nonce; + *__unwrap_nonce += 1; + drop(__unwrap_nonce); + dtrace_entry("main:::ENTER", __daikon_nonce); + dtrace_newline(); + dtrace_exit("main:::EXIT1", __daikon_nonce); + dtrace_newline(); + return; +} diff --git a/daikon-tests/test/non-ret-if-else.rs b/daikon-tests/test/non-ret-if-else.rs new file mode 100644 index 0000000000000..2f3075612cb8b --- /dev/null +++ b/daikon-tests/test/non-ret-if-else.rs @@ -0,0 +1,15 @@ +/* + * Description: verify 22 and 19 are not classified as exit ppts + */ + +fn boop(x: i32) -> i32 { + let y = + if x == 12 { + 22 + } else { + 19 + }; + return y; +} + +fn main() {}