]> git.lizzy.rs Git - rust.git/commitdiff
core: make the public fmt API completely safe.
authorEduard Burtescu <edy.burt@gmail.com>
Wed, 3 Dec 2014 20:56:39 +0000 (22:56 +0200)
committerEduard Burtescu <edy.burt@gmail.com>
Mon, 8 Dec 2014 07:14:21 +0000 (09:14 +0200)
src/libcore/fmt/mod.rs
src/libsyntax/ext/format.rs

index 6180daebcc2d4580d3d64aa52c9d516e7c48f903..8b2ffd90ef71529dd948d7033633f47a46fa4e67 100644 (file)
@@ -92,6 +92,9 @@ pub struct Formatter<'a> {
     args: &'a [Argument<'a>],
 }
 
+// NB. Argument is essentially an optimized partially applied formatting function,
+// equivalent to `exists T.(&T, fn(&T, &mut Formatter) -> Result`.
+
 enum Void {}
 
 /// This struct represents the generic "argument" which is taken by the Xprintf
@@ -100,21 +103,47 @@ enum Void {}
 /// types, and then this struct is used to canonicalize arguments to one type.
 #[experimental = "implementation detail of the `format_args!` macro"]
 pub struct Argument<'a> {
-    formatter: extern "Rust" fn(&Void, &mut Formatter) -> Result,
     value: &'a Void,
+    formatter: fn(&Void, &mut Formatter) -> Result,
+}
+
+impl<'a> Argument<'a> {
+    #[inline(never)]
+    fn show_uint(x: &uint, f: &mut Formatter) -> Result {
+        Show::fmt(x, f)
+    }
+
+    fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter) -> Result) -> Argument<'a> {
+        unsafe {
+            Argument {
+                formatter: mem::transmute(f),
+                value: mem::transmute(x)
+            }
+        }
+    }
+
+    fn from_uint<'a>(x: &'a uint) -> Argument<'a> {
+        Argument::new(x, Argument::show_uint)
+    }
+
+    fn as_uint(&self) -> Option<uint> {
+        if self.formatter as uint == Argument::show_uint as uint {
+            Some(unsafe { *(self.value as *const _ as *const uint) })
+        } else {
+            None
+        }
+    }
 }
 
 impl<'a> Arguments<'a> {
     /// When using the format_args!() macro, this function is used to generate the
-    /// Arguments structure. The compiler inserts an `unsafe` block to call this,
-    /// which is valid because the compiler performs all necessary validation to
-    /// ensure that the resulting call to format/write would be safe.
+    /// Arguments structure.
     #[doc(hidden)] #[inline]
     #[experimental = "implementation detail of the `format_args!` macro"]
-    pub unsafe fn new<'a>(pieces: &'static [&'static str],
-                          args: &'a [Argument<'a>]) -> Arguments<'a> {
+    pub fn new<'a>(pieces: &'a [&'a str],
+                   args: &'a [Argument<'a>]) -> Arguments<'a> {
         Arguments {
-            pieces: mem::transmute(pieces),
+            pieces: pieces,
             fmt: None,
             args: args
         }
@@ -122,15 +151,18 @@ pub unsafe fn new<'a>(pieces: &'static [&'static str],
 
     /// This function is used to specify nonstandard formatting parameters.
     /// The `pieces` array must be at least as long as `fmt` to construct
-    /// a valid Arguments structure.
+    /// a valid Arguments structure. Also, any `Count` within `fmt` that is
+    /// `CountIsParam` or `CountIsNextParam` has to point to an argument
+    /// created with `argumentuint`. However, failing to do so doesn't cause
+    /// unsafety, but will ignore invalid .
     #[doc(hidden)] #[inline]
     #[experimental = "implementation detail of the `format_args!` macro"]
-    pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
-                                        fmt: &'static [rt::Argument<'static>],
-                                        args: &'a [Argument<'a>]) -> Arguments<'a> {
+    pub fn with_placeholders<'a>(pieces: &'a [&'a str],
+                                 fmt: &'a [rt::Argument<'a>],
+                                 args: &'a [Argument<'a>]) -> Arguments<'a> {
         Arguments {
-            pieces: mem::transmute(pieces),
-            fmt: Some(mem::transmute(fmt)),
+            pieces: pieces,
+            fmt: Some(fmt),
             args: args
         }
     }
@@ -312,15 +344,13 @@ fn run(&mut self, arg: &rt::Argument) -> Result {
 
     fn getcount(&mut self, cnt: &rt::Count) -> Option<uint> {
         match *cnt {
-            rt::CountIs(n) => { Some(n) }
-            rt::CountImplied => { None }
+            rt::CountIs(n) => Some(n),
+            rt::CountImplied => None,
             rt::CountIsParam(i) => {
-                let v = self.args[i].value;
-                unsafe { Some(*(v as *const _ as *const uint)) }
+                self.args[i].as_uint()
             }
             rt::CountIsNextParam => {
-                let v = self.curarg.next().unwrap().value;
-                unsafe { Some(*(v as *const _ as *const uint)) }
+                self.curarg.next().and_then(|arg| arg.as_uint())
             }
         }
     }
@@ -533,22 +563,17 @@ fn fmt(&self, f: &mut Formatter) -> Result {
 /// create the Argument structures that are passed into the `format` function.
 #[doc(hidden)] #[inline]
 #[experimental = "implementation detail of the `format_args!` macro"]
-pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter) -> Result,
+pub fn argument<'a, T>(f: fn(&T, &mut Formatter) -> Result,
                        t: &'a T) -> Argument<'a> {
-    unsafe {
-        Argument {
-            formatter: mem::transmute(f),
-            value: mem::transmute(t)
-        }
-    }
+    Argument::new(t, f)
 }
 
 /// When the compiler determines that the type of an argument *must* be a uint
-/// (such as for plural), then it invokes this method.
+/// (such as for width and precision), then it invokes this method.
 #[doc(hidden)] #[inline]
 #[experimental = "implementation detail of the `format_args!` macro"]
 pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
-    argument(Show::fmt, s)
+    Argument::from_uint(s)
 }
 
 // Implementations of the core formatting traits
index d7d6c636849a66a27fdae1ebeae2c77b9a266340..c8fed3dcd16f6f092bdee2ef7cf1adad042e2669 100644 (file)
@@ -577,17 +577,11 @@ fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
         }
 
         // Now create a vector containing all the arguments
-        let slicename = self.ecx.ident_of("__args_vec");
-        {
-            let args = names.into_iter().map(|a| a.unwrap());
-            let args = locals.into_iter().chain(args);
-            let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
-            lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
-        }
+        let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
 
         // Now create the fmt::Arguments struct with all our locals we created.
         let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
-        let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
+        let args_slice = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
 
         let (fn_name, fn_args) = if self.all_pieces_simple {
             ("new", vec![pieces, args_slice])
@@ -602,29 +596,18 @@ fn to_expr(mut self, invocation: Invocation) -> P<ast::Expr> {
                 self.ecx.ident_of("Arguments"),
                 self.ecx.ident_of(fn_name)), fn_args);
 
-        // We did all the work of making sure that the arguments
-        // structure is safe, so we can safely have an unsafe block.
-        let result = self.ecx.expr_block(P(ast::Block {
-           view_items: Vec::new(),
-           stmts: Vec::new(),
-           expr: Some(result),
-           id: ast::DUMMY_NODE_ID,
-           rules: ast::UnsafeBlock(ast::CompilerGenerated),
-           span: self.fmtsp,
-        }));
-        let resname = self.ecx.ident_of("__args");
-        lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
-        let res = self.ecx.expr_ident(self.fmtsp, resname);
         let result = match invocation {
             Call(e) => {
                 let span = e.span;
-                self.ecx.expr_call(span, e,
-                                   vec!(self.ecx.expr_addr_of(span, res)))
+                self.ecx.expr_call(span, e, vec![
+                    self.ecx.expr_addr_of(span, result)
+                ])
             }
             MethodCall(e, m) => {
                 let span = e.span;
-                self.ecx.expr_method_call(span, e, m,
-                                          vec!(self.ecx.expr_addr_of(span, res)))
+                self.ecx.expr_method_call(span, e, m, vec![
+                    self.ecx.expr_addr_of(span, result)
+                ])
             }
         };
         let body = self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,