1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 * rusti - A REPL using the JIT backend
14 * Rusti works by serializing state between lines of input. This means that each
15 * line can be run in a separate task, and the only limiting factor is that all
16 * local bound variables are encodable.
18 * This is accomplished by feeding in generated input to rustc for execution in
19 * the JIT compiler. Currently input actually gets fed in three times to get
20 * information about the program.
23 * In this pass, the input is simply thrown at the parser and the input comes
24 * back. This validates the structure of the program, and at this stage the
25 * global items (fns, structs, impls, traits, etc.) are filtered from the
26 * input into the "global namespace". These declarations shadow all previous
27 * declarations of an item by the same name.
30 * After items have been stripped, the remaining input is passed to rustc
31 * along with all local variables declared (initialized to nothing). This pass
32 * runs up to typechecking. From this, we can learn about the types of each
33 * bound variable, what variables are bound, and also ensure that all the
34 * types are encodable (the input can actually be run).
37 * Finally, a program is generated to deserialize the local variable state,
38 * run the code input, and then reserialize all bindings back into a local
39 * hash map. This code is then run in the JIT engine provided by the rust
43 * Once this code runs, the input has fully been run and the hash map of local
44 * variables from TLS is read back into the local store of variables. This is
45 * then used later to pass back along to the parent rusti task and then begin
46 * waiting for input again.
49 * When running rusti code, it's important to consume ownership of the LLVM
50 * jit contextual information to prevent code from being deallocated too soon
51 * (before drop glue runs, see #7732). For this reason, the jit context is
52 * consumed and also passed along to the parent task. The parent task then
53 * keeps around all contexts while rusti is running. This must be done because
54 * tasks could in theory be spawned off and running in the background (still
57 * Encoding/decoding is done with EBML, and there is simply a map of ~str ->
58 * ~[u8] maintaining the values of each local binding (by name).
61 #[link(name = "rusti",
63 uuid = "7fb5bf52-7d45-4fee-8325-5ad3311149fc",
64 url = "https://github.com/mozilla/rust/tree/master/src/rusti")];
66 #[license = "MIT/ASL2"];
67 #[crate_type = "lib"];
73 use std::{libc, io, os, task};
77 use rustc::driver::{driver, session};
78 use rustc::back::link::jit;
79 use syntax::{ast, diagnostic};
80 use syntax::ast_util::*;
81 use syntax::parse::token;
82 use syntax::print::pprust;
91 * A structure shared across REPL instances for storing history
92 * such as statements and view items. I wish the AST was sendable.
98 lib_search_paths: ~[~str],
99 engines: ~[~jit::Engine],
104 // Action to do after reading a :command
107 action_run_line(~str),
110 /// Run an input string in a Repl, returning the new Repl.
111 fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str],
112 input: ~str) -> (~Program, Option<~jit::Engine>)
114 // Build some necessary rustc boilerplate for compiling things
115 let binary = binary.to_managed();
116 let options = @session::options {
117 crate_type: session::unknown_crate,
119 addl_lib_search_paths: @mut lib_search_paths.map(|p| Path(*p)),
121 .. (*session::basic_options()).clone()
123 // Because we assume that everything is encodable (and assert so), add some
124 // extra helpful information if the error crops up. Otherwise people are
125 // bound to be very confused when they find out code is running that they
127 let sess = driver::build_session(options, |cm, msg, lvl| {
128 diagnostic::emit(cm, msg, lvl);
129 if msg.contains("failed to find an implementation of trait") &&
130 msg.contains("extra::serialize::Encodable") {
132 "Currrently rusti serializes bound locals between \
133 different lines of input. This means that all \
134 values of local variables need to be encodable, \
135 and this type isn't encodable",
139 let intr = token::get_ident_interner();
142 // Stage 1: parse the input and filter it into the program (as necessary)
144 debug!("parsing: %s", input);
145 let crate = parse_input(sess, binary, input);
146 let mut to_run = ~[]; // statements to run (emitted back into code)
147 let new_locals = @mut ~[]; // new locals being defined
148 let mut result = None; // resultant expression (to print via pp)
149 do find_main(crate, sess) |blk| {
150 // Fish out all the view items, be sure to record 'extern mod' items
151 // differently beause they must appear before all 'use' statements
152 for blk.view_items.iter().advance |vi| {
153 let s = do with_pp(intr) |pp, _| {
154 pprust::print_view_item(pp, vi);
157 ast::view_item_extern_mod(*) => {
158 program.record_extern(s);
160 ast::view_item_use(*) => { program.record_view_item(s); }
164 // Iterate through all of the block's statements, inserting them into
165 // the correct portions of the program
166 for blk.stmts.iter().advance |stmt| {
167 let s = do with_pp(intr) |pp, _| { pprust::print_stmt(pp, *stmt); };
169 ast::stmt_decl(d, _) => {
171 ast::decl_item(it) => {
172 let name = sess.str_of(it.ident);
174 // Structs are treated specially because to make
175 // them at all usable they need to be decorated
176 // with #[deriving(Encoable, Decodable)]
177 ast::item_struct(*) => {
178 program.record_struct(name, s);
180 // Item declarations are hoisted out of main()
181 _ => { program.record_item(name, s); }
185 // Local declarations must be specially dealt with,
186 // record all local declarations for use later on
187 ast::decl_local(l) => {
188 let mutbl = l.is_mutbl;
189 do each_binding(l) |path, _| {
190 let s = do with_pp(intr) |pp, _| {
191 pprust::print_path(pp, path, false);
193 new_locals.push((s, mutbl));
200 // run statements with expressions (they have effects)
201 ast::stmt_mac(*) | ast::stmt_semi(*) | ast::stmt_expr(*) => {
206 result = do blk.expr.map_consume |e| {
207 do with_pp(intr) |pp, _| { pprust::print_expr(pp, e); }
210 // return fast for empty inputs
211 if to_run.len() == 0 && result.is_none() {
212 return (program, None);
216 // Stage 2: run everything up to typeck to learn the types of the new
217 // variables introduced into the program
219 info!("Learning about the new types in the program");
220 program.set_cache(); // before register_new_vars (which changes them)
221 let input = to_run.connect("\n");
222 let test = program.test_code(input, &result, *new_locals);
223 debug!("testing with ^^^^^^ %?", (||{ println(test) })());
224 let dinput = driver::str_input(test.to_managed());
225 let cfg = driver::build_configuration(sess, binary, &dinput);
226 let outputs = driver::build_output_filenames(&dinput, &None, &None, [], sess);
227 let (crate, tcx) = driver::compile_upto(sess, cfg.clone(), &dinput,
228 driver::cu_typeck, Some(outputs));
229 // Once we're typechecked, record the types of all local variables defined
231 do find_main(crate.expect("crate after cu_typeck"), sess) |blk| {
232 program.register_new_vars(blk, tcx.expect("tcx after cu_typeck"));
236 // Stage 3: Actually run the code in the JIT
238 info!("actually running code");
239 let code = program.code(input, &result);
240 debug!("actually running ^^^^^^ %?", (||{ println(code) })());
241 let input = driver::str_input(code.to_managed());
242 let cfg = driver::build_configuration(sess, binary, &input);
243 let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
244 let sess = driver::build_session(options, diagnostic::emit);
245 driver::compile_upto(sess, cfg, &input, driver::cu_everything,
249 // Stage 4: Inform the program that computation is done so it can update all
250 // local variable bindings.
252 info!("cleaning up after code");
253 program.consume_cache();
256 // Stage 5: Extract the LLVM execution engine to take ownership of the
257 // generated JIT code. This means that rusti can spawn parallel
258 // tasks and we won't deallocate the code emitted until rusti
259 // itself is destroyed.
261 return (program, jit::consume_engine());
263 fn parse_input(sess: session::Session, binary: @str,
264 input: &str) -> @ast::Crate {
265 let code = fmt!("fn main() {\n %s \n}", input);
266 let input = driver::str_input(code.to_managed());
267 let cfg = driver::build_configuration(sess, binary, &input);
268 let outputs = driver::build_output_filenames(&input, &None, &None, [], sess);
269 let (crate, _) = driver::compile_upto(sess, cfg, &input,
270 driver::cu_parse, Some(outputs));
271 crate.expect("parsing should return a crate")
274 fn find_main(crate: @ast::Crate, sess: session::Session,
275 f: &fn(&ast::Block)) {
276 for crate.module.items.iter().advance |item| {
278 ast::item_fn(_, _, _, _, ref blk) => {
279 if item.ident == sess.ident_of("main") {
286 fail!("main function was expected somewhere...");
290 // Compiles a crate given by the filename as a library if the compiled
291 // version doesn't exist or is older than the source file. Binary is
292 // the name of the compiling executable. Returns Some(true) if it
293 // successfully compiled, Some(false) if the crate wasn't compiled
294 // because it already exists and is newer than the source file, or
295 // None if there were compile errors.
296 fn compile_crate(src_filename: ~str, binary: ~str) -> Option<bool> {
298 let src_path = Path(src_filename);
299 let binary = binary.to_managed();
300 let options = @session::options {
302 addl_lib_search_paths: @mut ~[os::getcwd()],
303 .. (*session::basic_options()).clone()
305 let input = driver::file_input(src_path.clone());
306 let sess = driver::build_session(options, diagnostic::emit);
307 *sess.building_library = true;
308 let cfg = driver::build_configuration(sess, binary, &input);
309 let outputs = driver::build_output_filenames(
310 &input, &None, &None, [], sess);
311 // If the library already exists and is newer than the source
312 // file, skip compilation and return None.
313 let mut should_compile = true;
314 let dir = os::list_dir_path(&Path(outputs.out_filename.dirname()));
315 let maybe_lib_path = do dir.iter().find_ |file| {
316 // The actual file's name has a hash value and version
317 // number in it which is unknown at this time, so looking
318 // for a file that matches out_filename won't work,
319 // instead we guess which file is the library by matching
320 // the prefix and suffix of out_filename to files in the
322 let file_str = file.filename().get();
323 file_str.starts_with(outputs.out_filename.filestem().get())
324 && file_str.ends_with(outputs.out_filename.filetype().get())
326 match maybe_lib_path {
328 let (src_mtime, _) = src_path.get_mtime().get();
329 let (lib_mtime, _) = lib_path.get_mtime().get();
330 if lib_mtime >= src_mtime {
331 should_compile = false;
336 if (should_compile) {
337 printfln!("compiling %s...", src_filename);
338 driver::compile_upto(sess, cfg, &input, driver::cu_everything,
343 Ok(true) => Some(true),
344 Ok(false) => Some(false),
349 /// Tries to get a line from rl after outputting a prompt. Returns
350 /// None if no input was read (e.g. EOF was reached).
351 fn get_line(use_rl: bool, prompt: &str) -> Option<~str> {
353 let result = unsafe { rl::read(prompt) };
358 unsafe { rl::add_history(line) };
363 if io::stdin().eof() {
366 Some(io::stdin().read_line())
371 /// Run a command, e.g. :clear, :exit, etc.
372 fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer,
373 cmd: ~str, args: ~[~str], use_rl: bool) -> CmdAction {
374 let mut action = action_none;
376 ~"exit" => repl.running = false,
378 repl.program.clear();
380 // XXX: Win32 version of linenoise can't do this
385 ":{\\n ..lines.. \\n:}\\n - execute multiline command\n\
386 :load <crate> ... - loads given crates as dynamic libraries\n\
387 :clear - clear the bindings\n\
388 :exit - exit from the repl\n\
389 :help - show this message");
392 let mut loaded_crates: ~[~str] = ~[];
393 for args.iter().advance |arg| {
394 let (crate, filename) =
395 if arg.ends_with(".rs") || arg.ends_with(".rc") {
396 (arg.slice_to(arg.len() - 3).to_owned(), (*arg).clone())
398 ((*arg).clone(), *arg + ".rs")
400 match compile_crate(filename, repl.binary.clone()) {
401 Some(_) => loaded_crates.push(crate),
405 for loaded_crates.iter().advance |crate| {
406 let crate_path = Path(*crate);
407 let crate_dir = crate_path.dirname();
408 repl.program.record_extern(fmt!("extern mod %s;", *crate));
409 if !repl.lib_search_paths.iter().any(|x| x == &crate_dir) {
410 repl.lib_search_paths.push(crate_dir);
413 if loaded_crates.is_empty() {
414 println("no crates loaded");
416 printfln!("crates loaded: %s", loaded_crates.connect(", "));
420 let mut multiline_cmd = ~"";
421 let mut end_multiline = false;
422 while (!end_multiline) {
423 match get_line(use_rl, "rusti| ") {
424 None => fail!("unterminated multiline command :{ .. :}"),
426 if line.trim() == ":}" {
427 end_multiline = true;
429 multiline_cmd.push_str(line);
430 multiline_cmd.push_char('\n');
435 action = action_run_line(multiline_cmd);
437 _ => println(~"unknown cmd: " + cmd)
442 /// Executes a line of input, which may either be rust code or a
443 /// :command. Returns a new Repl if it has changed.
444 pub fn run_line(repl: &mut Repl, in: @io::Reader, out: @io::Writer, line: ~str,
445 use_rl: bool) -> bool
447 if line.starts_with(":") {
448 // drop the : and the \n (one byte each)
449 let full = line.slice(1, line.len());
450 let split: ~[~str] = full.word_iter().transform(|s| s.to_owned()).collect();
451 let len = split.len();
454 let cmd = split[0].clone();
457 let args = if len > 1 {
458 split.slice(1, len).to_owned()
461 match run_cmd(repl, in, out, cmd, args, use_rl) {
463 action_run_line(multiline_cmd) => {
464 if !multiline_cmd.is_empty() {
465 return run_line(repl, in, out, multiline_cmd, use_rl);
474 let line = Cell::new(line);
475 let program = Cell::new(repl.program.clone());
476 let lib_search_paths = Cell::new(repl.lib_search_paths.clone());
477 let binary = Cell::new(repl.binary.clone());
478 let result = do task::try {
479 run(program.take(), binary.take(), lib_search_paths.take(), line.take())
483 Ok((program, engine)) => {
484 repl.program = program;
486 Some(e) => { repl.engines.push(e); }
491 Err(*) => { return false; }
496 let args = os::args();
497 let in = io::stdin();
498 let out = io::stdout();
499 let mut repl = Repl {
501 binary: args[0].clone(),
503 lib_search_paths: ~[],
506 program: ~Program::new(),
509 let istty = unsafe { libc::isatty(libc::STDIN_FILENO as i32) } != 0;
511 // only print this stuff if the user is actually typing into rusti
513 println("WARNING: The Rust REPL is experimental and may be");
514 println("unstable. If you encounter problems, please use the");
515 println("compiler instead. Type :help for help.");
518 do rl::complete |line, suggest| {
519 if line.starts_with(":") {
530 match get_line(istty, repl.prompt) {
539 run_line(&mut repl, in, out, line, istty);
548 use program::Program;
556 lib_search_paths: ~[],
558 program: ~Program::new(),
562 // FIXME: #7220 rusti on 32bit mac doesn't work.
563 // FIXME: #7641 rusti on 32bit linux cross compile doesn't work
564 // FIXME: #7115 re-enable once LLVM has been upgraded
565 #[cfg(thiswillneverbeacfgflag)]
566 fn run_program(prog: &str) {
568 for prog.split_iter('\n').advance |cmd| {
569 assert!(run_line(&mut r, io::stdin(), io::stdout(),
570 cmd.to_owned(), false),
571 "the command '%s' failed", cmd);
574 fn run_program(_: &str) {}
582 fn regression_5937() {
583 run_program("use std::hashmap;");
587 fn regression_5784() {
588 run_program("let a = 3;");
593 // XXX: can't spawn new tasks because the JIT code is cleaned up
594 // after the main function is done.
596 spawn( || println(\"Please don't segfault\") );
597 do spawn { println(\"Please?\"); }
602 fn inferred_integers_usable() {
603 run_program("let a = 2;\n()\n");
607 assert!((a as uint) + b == 7)
612 fn local_variables_allow_shadowing() {
627 assert!(d.len() == 0);
632 fn vectors_usable() {
638 assert!(d.len() == 9);
644 fn structs_usable() {
653 fn mutable_variables_work() {
657 let mut b = std::hashmap::HashSet::new::<int>();
659 assert!(b.contains(&5))
660 assert!(b.len() == 1)
665 fn functions_saved() {
667 fn fib(x: int) -> int { if x < 2 {x} else { fib(x - 1) + fib(x - 2) } }
677 mod b { pub fn foo() -> uint { 3 } }
678 assert!(b::foo() == 3)
683 fn multiple_functions() {
692 fn multiple_items_same_name() {
704 fn simultaneous_definition_and_expression() {
714 let result = run_line(&mut r, io::stdin(), io::stdout(),