// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::cast; use std::hashmap::HashMap; use std::local_data; use std::sys; use syntax::ast; use syntax::parse::token; use syntax::print::pprust; use rustc::middle::ty; use rustc::util::ppaux; use utils::*; /// This structure keeps track of the state of the world for the code being /// executed in rusti. struct Program { /// All known local variables local_vars: HashMap<~str, LocalVariable>, /// New variables which will be present (learned from typechecking) newvars: HashMap<~str, LocalVariable>, /// All known view items (use statements), distinct because these must /// follow extern mods view_items: ~str, /// All known 'extern mod' statements (must always come first) externs: ~str, /// All known structs defined. These need to have /// #[deriving(Encodable,Decodable)] to be at all useful in rusti structs: HashMap<~str, ~str>, /// All other items, can all be intermingled. Duplicate definitions of the /// same name have the previous one overwritten. items: HashMap<~str, ~str>, } /// Represents a local variable that the program is currently using. struct LocalVariable { /// Should this variable be locally declared as mutable? mutable: bool, /// This is the type of the serialized data below ty: ~str, /// This is the serialized version of the variable data: ~[u8], /// When taking borrowed pointers or slices, care must be taken to ensure /// that the deserialization produces what we'd expect. If some magic is in /// order, the first element of this pair is the actual type of the local /// variable (which can be different from the deserialized type), and the /// second element are the '&'s which need to be prepended. alterations: Option<(~str, ~str)>, } type LocalCache = @mut HashMap<~str, @~[u8]>; fn tls_key(_k: @LocalCache) {} impl Program { pub fn new() -> Program { Program { local_vars: HashMap::new(), newvars: HashMap::new(), view_items: ~"", externs: ~"", structs: HashMap::new(), items: HashMap::new(), } } /// Clears all local bindings about variables, items, externs, etc. pub fn clear(&mut self) { *self = Program::new(); } /// Creates a block of code to be fed to rustc. This code is not meant to /// run, but rather it is meant to learn about the input given. This will /// assert that the types of all bound local variables are encodable, /// along with checking syntax and other rust-related things. The reason /// that we only check for encodability is that some super-common types /// (like &'static str) are not decodable, but are encodable. By doing some /// mild approximation when decoding, we can emulate at least &str and &[T]. /// /// Once this code has been fed to rustc, it is intended that the code() /// function is used to actually generate code to fully compile and run. pub fn test_code(&self, user_input: &str, to_print: &Option<~str>, new_locals: &[(~str, bool)]) -> ~str { let mut code = self.program_header(); code.push_str(" fn assert_encodable>(t: &T) {} "); code.push_str("fn main() {\n"); // It's easy to initialize things if we don't run things... for self.local_vars.each |name, var| { let mt = var.mt(); code.push_str(fmt!("let%s %s: %s = fail!();\n", mt, *name, var.ty)); var.alter(*name, &mut code); } code.push_str("{\n"); code.push_str(user_input); code.push_char('\n'); match *to_print { Some(ref s) => { code.push_str(*s); code.push_char('\n'); } None => {} } for new_locals.each |p| { code.push_str(fmt!("assert_encodable(&%s);\n", *p.first_ref())); } code.push_str("};}"); return code; } /// Creates a program to be fed into rustc. This program is structured to /// deserialize all bindings into local variables, run the code input, and /// then reserialize all the variables back out. /// /// This program (unlike test_code) is meant to run to actually execute the /// user's input pub fn code(&mut self, user_input: &str, to_print: &Option<~str>) -> ~str { let mut code = self.program_header(); code.push_str(" fn main() { "); let key: sys::Closure = unsafe { let tls_key: &'static fn(@LocalCache) = tls_key; cast::transmute(tls_key) }; // First, get a handle to the tls map which stores all the local // variables. This works by totally legitimately using the 'code' // pointer of the 'tls_key' function as a uint, and then casting it back // up to a function code.push_str(fmt!(" let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe { let key = ::std::sys::Closure{ code: %? as *(), env: ::std::ptr::null() }; let key = ::std::cast::transmute(key); *::std::local_data::local_data_get(key).unwrap() };\n", key.code as uint)); // Using this __tls_map handle, deserialize each variable binding that // we know about for self.local_vars.each |name, var| { let mt = var.mt(); code.push_str(fmt!("let%s %s: %s = { let data = __tls_map.get_copy(&~\"%s\"); let doc = ::extra::ebml::reader::Doc(data); let mut decoder = ::extra::ebml::reader::Decoder(doc); ::extra::serialize::Decodable::decode(&mut decoder) };\n", mt, *name, var.ty, *name)); var.alter(*name, &mut code); } // After all that, actually run the user's code. code.push_str(user_input); code.push_char('\n'); match *to_print { Some(ref s) => { code.push_str(fmt!("pp({\n%s\n});", *s)); } None => {} } do self.newvars.consume |name, var| { self.local_vars.insert(name, var); } // After the input code is run, we can re-serialize everything back out // into tls map (to be read later on by this task) for self.local_vars.each |name, var| { code.push_str(fmt!("{ let local: %s = %s; let bytes = do ::std::io::with_bytes_writer |io| { let mut enc = ::extra::ebml::writer::Encoder(io); local.encode(&mut enc); }; __tls_map.insert(~\"%s\", @bytes); }\n", var.real_ty(), *name, *name)); } // Close things up, and we're done. code.push_str("}"); return code; } /// Creates the header of the programs which are generated to send to rustc fn program_header(&self) -> ~str { // up front, disable lots of annoying lints, then include all global // state such as items, view items, and extern mods. let mut code = fmt!(" #[allow(ctypes)]; #[allow(heap_memory)]; #[allow(implicit_copies)]; #[allow(managed_heap_memory)]; #[allow(non_camel_case_types)]; #[allow(owned_heap_memory)]; #[allow(path_statement)]; #[allow(unrecognized_lint)]; #[allow(unused_imports)]; #[allow(while_true)]; #[allow(unused_variable)]; #[allow(dead_assignment)]; #[allow(unused_unsafe)]; #[allow(unused_mut)]; #[allow(unreachable_code)]; extern mod extra; %s // extern mods use extra::serialize::*; %s // view items ", self.externs, self.view_items); for self.structs.each_value |s| { // The structs aren't really useful unless they're encodable code.push_str("#[deriving(Encodable, Decodable)]"); code.push_str(*s); code.push_str("\n"); } for self.items.each_value |s| { code.push_str(*s); code.push_str("\n"); } code.push_str("fn pp(t: T) { println(fmt!(\"%?\", t)); }\n"); return code; } /// Initializes the task-local cache of all local variables known to the /// program. This will be used to read local variables out of once the /// program starts pub fn set_cache(&self) { let map = @mut HashMap::new(); for self.local_vars.each |name, value| { map.insert(copy *name, @copy value.data); } unsafe { local_data::local_data_set(tls_key, @map); } } /// Once the program has finished running, this function will consume the /// task-local cache of local variables. After the program finishes running, /// it updates this cache with the new values of each local variable. pub fn consume_cache(&mut self) { let map = unsafe { local_data::local_data_pop(tls_key).expect("tls is empty") }; do map.consume |name, value| { match self.local_vars.find_mut(&name) { Some(v) => { v.data = copy *value; } None => { fail!("unknown variable %s", name) } } } } // Simple functions to record various global things (as strings) pub fn record_view_item(&mut self, vi: &str) { self.view_items.push_str(vi); self.view_items.push_char('\n'); } pub fn record_struct(&mut self, name: &str, s: ~str) { let name = name.to_owned(); self.items.remove(&name); self.structs.insert(name, s); } pub fn record_item(&mut self, name: &str, it: ~str) { let name = name.to_owned(); self.structs.remove(&name); self.items.insert(name, it); } pub fn record_extern(&mut self, name: &str) { self.externs.push_str(name); self.externs.push_char('\n'); } /// This monster function is responsible for reading the main function /// generated by test_code() to determine the type of each local binding /// created by the user's input. /// /// Once the types are known, they are inserted into the local_vars map in /// this Program (to be deserialized later on pub fn register_new_vars(&mut self, blk: &ast::blk, tcx: ty::ctxt) { debug!("looking for new variables"); let newvars = @mut HashMap::new(); do each_user_local(blk) |local| { let mutable = local.node.is_mutbl; do each_binding(local) |path, id| { let name = do with_pp(token::get_ident_interner()) |pp, _| { pprust::print_path(pp, path, false); }; let mut t = ty::node_id_to_type(tcx, id); let mut tystr = ~""; let mut lvar = LocalVariable { ty: ~"", data: ~[], mutable: mutable, alterations: None, }; // This loop is responsible for figuring out what "alterations" // are necessary for this local variable. loop { match ty::get(t).sty { // &T encoded will decode to T, so we need to be sure to // re-take a loan after decoding ty::ty_rptr(_, mt) => { if mt.mutbl == ast::m_mutbl { tystr.push_str("&mut "); } else { tystr.push_str("&"); } t = mt.ty; } // Literals like [1, 2, 3] and (~[0]).slice() will both // be serialized to ~[T], whereas it's requested to be a // &[T] instead. ty::ty_evec(mt, ty::vstore_slice(*)) | ty::ty_evec(mt, ty::vstore_fixed(*)) => { let vty = ppaux::ty_to_str(tcx, mt.ty); let derefs = copy tystr; lvar.ty = tystr + "~[" + vty + "]"; lvar.alterations = Some((tystr + "&[" + vty + "]", derefs)); break; } // Similar to vectors, &str serializes to ~str, so a // borrow must be taken ty::ty_estr(ty::vstore_slice(*)) => { let derefs = copy tystr; lvar.ty = tystr + "~str"; lvar.alterations = Some((tystr + "&str", derefs)); break; } // Don't generate extra stuff if there's no borrowing // going on here _ if "" == tystr => { lvar.ty = ppaux::ty_to_str(tcx, t); break; } // If we're just borrowing (no vectors or strings), then // we just need to record how many borrows there were. _ => { let derefs = copy tystr; let tmptystr = ppaux::ty_to_str(tcx, t); lvar.alterations = Some((tystr + tmptystr, derefs)); lvar.ty = tmptystr; break; } } } newvars.insert(name, lvar); } } // I'm not an @ pointer, so this has to be done outside. do newvars.consume |k, v| { self.newvars.insert(k, v); } // helper functions to perform ast iteration fn each_user_local(blk: &ast::blk, f: &fn(@ast::local)) { do find_user_block(blk) |blk| { for blk.node.stmts.each |stmt| { match stmt.node { ast::stmt_decl(d, _) => { match d.node { ast::decl_local(l) => { f(l); } _ => {} } } _ => {} } } } } fn find_user_block(blk: &ast::blk, f: &fn(&ast::blk)) { for blk.node.stmts.each |stmt| { match stmt.node { ast::stmt_semi(e, _) => { match e.node { ast::expr_block(ref blk) => { return f(blk); } _ => {} } } _ => {} } } fail!("couldn't find user block"); } } } impl LocalVariable { /// Performs alterations to the code provided, given the name of this /// variable. fn alter(&self, name: &str, code: &mut ~str) { match self.alterations { Some((ref real_ty, ref prefix)) => { code.push_str(fmt!("let%s %s: %s = %s%s;\n", self.mt(), name, *real_ty, *prefix, name)); } None => {} } } fn real_ty<'a>(&'a self) -> &'a str { match self.alterations { Some((ref real_ty, _)) => { let ret: &'a str = *real_ty; return ret; } None => { let ret: &'a str = self.ty; return ret; } } } fn mt(&self) -> &'static str { if self.mutable {" mut"} else {""} } }