mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
core: make the public fmt API completely safe.
This commit is contained in:
parent
c75e8d46c2
commit
fe4fdcc0f6
@ -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 @@ impl<'a> Arguments<'a> {
|
||||
|
||||
/// 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 @@ impl<'a> Formatter<'a> {
|
||||
|
||||
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 @@ impl Show for Error {
|
||||
/// 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
|
||||
|
@ -577,17 +577,11 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
}
|
||||
|
||||
// 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 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user