std: Modernize the local_data api
This commit brings the local_data api up to modern rust standards with a few key improvements: * The `pop` and `set` methods have been combined into one method, `replace` * The `get_mut` method has been removed. All interior mutability should be done through `RefCell`. * All functionality is now exposed as a method on the keys themselves. Instead of importing std::local_data, you now use "key.replace()" and "key.get()". * All closures have been removed in favor of RAII functionality. This means that get() and get_mut() no long require closures, but rather return Option<SmartPointer> where the smart pointer takes care of relinquishing the borrow and also implements the necessary Deref traits * The modify() function was removed to cut the local_data interface down to its bare essentials (similarly to how RefCell removed set/get). [breaking-change]
This commit is contained in:
parent
ef6daf9935
commit
ab92ea526d
23 changed files with 444 additions and 661 deletions
|
@ -1617,8 +1617,7 @@ mod test_map {
|
||||||
use std::cmp::Equiv;
|
use std::cmp::Equiv;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::iter::{Iterator,range_inclusive,range_step_inclusive};
|
use std::iter::{Iterator,range_inclusive,range_step_inclusive};
|
||||||
use std::local_data;
|
use std::cell::RefCell;
|
||||||
use std::vec;
|
|
||||||
|
|
||||||
struct KindaIntLike(int);
|
struct KindaIntLike(int);
|
||||||
|
|
||||||
|
@ -1657,7 +1656,7 @@ mod test_map {
|
||||||
assert_eq!(*m.find(&2).unwrap(), 4);
|
assert_eq!(*m.find(&2).unwrap(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data_key!(drop_vector: vec::Vec<int>)
|
local_data_key!(drop_vector: RefCell<Vec<int>>)
|
||||||
|
|
||||||
#[deriving(Hash, Eq, TotalEq)]
|
#[deriving(Hash, Eq, TotalEq)]
|
||||||
struct Dropable {
|
struct Dropable {
|
||||||
|
@ -1667,8 +1666,8 @@ mod test_map {
|
||||||
|
|
||||||
impl Dropable {
|
impl Dropable {
|
||||||
fn new(k: uint) -> Dropable {
|
fn new(k: uint) -> Dropable {
|
||||||
local_data::get_mut(drop_vector,
|
let v = drop_vector.get().unwrap();
|
||||||
|v| { v.unwrap().as_mut_slice()[k] += 1; });
|
v.borrow_mut().as_mut_slice()[k] += 1;
|
||||||
|
|
||||||
Dropable { k: k }
|
Dropable { k: k }
|
||||||
}
|
}
|
||||||
|
@ -1676,23 +1675,23 @@ mod test_map {
|
||||||
|
|
||||||
impl Drop for Dropable {
|
impl Drop for Dropable {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
local_data::get_mut(drop_vector, |v|
|
let v = drop_vector.get().unwrap();
|
||||||
{ v.unwrap().as_mut_slice()[self.k] -= 1; });
|
v.borrow_mut().as_mut_slice()[self.k] -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drops() {
|
fn test_drops() {
|
||||||
local_data::set(drop_vector, vec::Vec::from_elem(200, 0));
|
drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0))));
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
|
|
||||||
local_data::get(drop_vector, |v| {
|
let v = drop_vector.get().unwrap();
|
||||||
for i in range(0u, 200) {
|
for i in range(0u, 200) {
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 0);
|
assert_eq!(v.borrow().as_slice()[i], 0);
|
||||||
}
|
}
|
||||||
});
|
drop(v);
|
||||||
|
|
||||||
for i in range(0u, 100) {
|
for i in range(0u, 100) {
|
||||||
let d1 = Dropable::new(i);
|
let d1 = Dropable::new(i);
|
||||||
|
@ -1700,11 +1699,11 @@ mod test_map {
|
||||||
m.insert(d1, d2);
|
m.insert(d1, d2);
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data::get(drop_vector, |v| {
|
let v = drop_vector.get().unwrap();
|
||||||
for i in range(0u, 200) {
|
for i in range(0u, 200) {
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 1);
|
assert_eq!(v.borrow().as_slice()[i], 1);
|
||||||
}
|
}
|
||||||
});
|
drop(v);
|
||||||
|
|
||||||
for i in range(0u, 50) {
|
for i in range(0u, 50) {
|
||||||
let k = Dropable::new(i);
|
let k = Dropable::new(i);
|
||||||
|
@ -1712,30 +1711,27 @@ mod test_map {
|
||||||
|
|
||||||
assert!(v.is_some());
|
assert!(v.is_some());
|
||||||
|
|
||||||
local_data::get(drop_vector, |v| {
|
let v = drop_vector.get().unwrap();
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 1);
|
assert_eq!(v.borrow().as_slice()[i], 1);
|
||||||
assert_eq!(v.unwrap().as_slice()[i+100], 1);
|
assert_eq!(v.borrow().as_slice()[i+100], 1);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data::get(drop_vector, |v| {
|
let v = drop_vector.get().unwrap();
|
||||||
for i in range(0u, 50) {
|
for i in range(0u, 50) {
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 0);
|
assert_eq!(v.borrow().as_slice()[i], 0);
|
||||||
assert_eq!(v.unwrap().as_slice()[i+100], 0);
|
assert_eq!(v.borrow().as_slice()[i+100], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in range(50u, 100) {
|
for i in range(50u, 100) {
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 1);
|
assert_eq!(v.borrow().as_slice()[i], 1);
|
||||||
assert_eq!(v.unwrap().as_slice()[i+100], 1);
|
assert_eq!(v.borrow().as_slice()[i+100], 1);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data::get(drop_vector, |v| {
|
let v = drop_vector.get().unwrap();
|
||||||
for i in range(0u, 200) {
|
for i in range(0u, 200) {
|
||||||
assert_eq!(v.unwrap().as_slice()[i], 0);
|
assert_eq!(v.borrow().as_slice()[i], 0);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -122,7 +122,6 @@ use std::cast;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::LineBufferedWriter;
|
use std::io::LineBufferedWriter;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::local_data;
|
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::rt;
|
use std::rt;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
@ -228,7 +227,7 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
|
||||||
// Completely remove the local logger from TLS in case anyone attempts to
|
// Completely remove the local logger from TLS in case anyone attempts to
|
||||||
// frob the slot while we're doing the logging. This will destroy any logger
|
// frob the slot while we're doing the logging. This will destroy any logger
|
||||||
// set during logging.
|
// set during logging.
|
||||||
let mut logger = local_data::pop(local_logger).unwrap_or_else(|| {
|
let mut logger = local_logger.replace(None).unwrap_or_else(|| {
|
||||||
box DefaultLogger { handle: io::stderr() } as Box<Logger:Send>
|
box DefaultLogger { handle: io::stderr() } as Box<Logger:Send>
|
||||||
});
|
});
|
||||||
logger.log(&LogRecord {
|
logger.log(&LogRecord {
|
||||||
|
@ -238,7 +237,7 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) {
|
||||||
module_path: loc.module_path,
|
module_path: loc.module_path,
|
||||||
line: loc.line,
|
line: loc.line,
|
||||||
});
|
});
|
||||||
local_data::set(local_logger, logger);
|
local_logger.replace(Some(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Getter for the global log level. This is a function so that it can be called
|
/// Getter for the global log level. This is a function so that it can be called
|
||||||
|
@ -250,9 +249,7 @@ pub fn log_level() -> u32 { unsafe { LOG_LEVEL } }
|
||||||
/// Replaces the task-local logger with the specified logger, returning the old
|
/// Replaces the task-local logger with the specified logger, returning the old
|
||||||
/// logger.
|
/// logger.
|
||||||
pub fn set_logger(logger: Box<Logger:Send>) -> Option<Box<Logger:Send>> {
|
pub fn set_logger(logger: Box<Logger:Send>) -> Option<Box<Logger:Send>> {
|
||||||
let prev = local_data::pop(local_logger);
|
local_logger.replace(Some(logger))
|
||||||
local_data::set(local_logger, logger);
|
|
||||||
return prev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A LogRecord is created by the logging macros, and passed as the only
|
/// A LogRecord is created by the logging macros, and passed as the only
|
||||||
|
|
|
@ -79,7 +79,6 @@ println!("{:?}", tuple_ptr)
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::io::IoResult;
|
use std::io::IoResult;
|
||||||
use std::kinds::marker;
|
use std::kinds::marker;
|
||||||
use std::local_data;
|
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
|
||||||
pub use isaac::{IsaacRng, Isaac64Rng};
|
pub use isaac::{IsaacRng, Isaac64Rng};
|
||||||
|
@ -581,9 +580,6 @@ pub struct TaskRng {
|
||||||
marker: marker::NoSend,
|
marker: marker::NoSend,
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to make space in TLS for a random number generator
|
|
||||||
local_data_key!(TASK_RNG_KEY: Box<TaskRngInner>)
|
|
||||||
|
|
||||||
/// Retrieve the lazily-initialized task-local random number
|
/// Retrieve the lazily-initialized task-local random number
|
||||||
/// generator, seeded by the system. Intended to be used in method
|
/// generator, seeded by the system. Intended to be used in method
|
||||||
/// chaining style, e.g. `task_rng().gen::<int>()`.
|
/// chaining style, e.g. `task_rng().gen::<int>()`.
|
||||||
|
@ -596,7 +592,10 @@ local_data_key!(TASK_RNG_KEY: Box<TaskRngInner>)
|
||||||
/// the same sequence always. If absolute consistency is required,
|
/// the same sequence always. If absolute consistency is required,
|
||||||
/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`.
|
/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`.
|
||||||
pub fn task_rng() -> TaskRng {
|
pub fn task_rng() -> TaskRng {
|
||||||
local_data::get_mut(TASK_RNG_KEY, |rng| match rng {
|
// used to make space in TLS for a random number generator
|
||||||
|
local_data_key!(TASK_RNG_KEY: Box<TaskRngInner>)
|
||||||
|
|
||||||
|
match TASK_RNG_KEY.get() {
|
||||||
None => {
|
None => {
|
||||||
let r = match StdRng::new() {
|
let r = match StdRng::new() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
@ -607,12 +606,15 @@ pub fn task_rng() -> TaskRng {
|
||||||
TaskRngReseeder);
|
TaskRngReseeder);
|
||||||
let ptr = &mut *rng as *mut TaskRngInner;
|
let ptr = &mut *rng as *mut TaskRngInner;
|
||||||
|
|
||||||
local_data::set(TASK_RNG_KEY, rng);
|
TASK_RNG_KEY.replace(Some(rng));
|
||||||
|
|
||||||
TaskRng { rng: ptr, marker: marker::NoSend }
|
TaskRng { rng: ptr, marker: marker::NoSend }
|
||||||
}
|
}
|
||||||
Some(rng) => TaskRng { rng: &mut **rng, marker: marker::NoSend }
|
Some(rng) => TaskRng {
|
||||||
})
|
rng: &**rng as *_ as *mut TaskRngInner,
|
||||||
|
marker: marker::NoSend
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rng for TaskRng {
|
impl Rng for TaskRng {
|
||||||
|
|
|
@ -74,7 +74,6 @@ use arena::TypedArena;
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use std::c_str::ToCStr;
|
use std::c_str::ToCStr;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::local_data;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic};
|
use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic};
|
||||||
use syntax::ast_util::{local_def, is_local};
|
use syntax::ast_util::{local_def, is_local};
|
||||||
|
@ -88,19 +87,17 @@ use syntax::{ast, ast_util, ast_map};
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
local_data_key!(task_local_insn_key: Vec<&'static str> )
|
local_data_key!(task_local_insn_key: RefCell<Vec<&'static str>>)
|
||||||
|
|
||||||
pub fn with_insn_ctxt(blk: |&[&'static str]|) {
|
pub fn with_insn_ctxt(blk: |&[&'static str]|) {
|
||||||
local_data::get(task_local_insn_key, |c| {
|
match task_local_insn_key.get() {
|
||||||
match c {
|
Some(ctx) => blk(ctx.borrow().as_slice()),
|
||||||
Some(ctx) => blk(ctx.as_slice()),
|
None => ()
|
||||||
None => ()
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_insn_ctxt() {
|
pub fn init_insn_ctxt() {
|
||||||
local_data::set(task_local_insn_key, Vec::new());
|
task_local_insn_key.replace(Some(RefCell::new(Vec::new())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct _InsnCtxt { _x: () }
|
pub struct _InsnCtxt { _x: () }
|
||||||
|
@ -108,23 +105,19 @@ pub struct _InsnCtxt { _x: () }
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl Drop for _InsnCtxt {
|
impl Drop for _InsnCtxt {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
local_data::modify(task_local_insn_key, |c| {
|
match task_local_insn_key.get() {
|
||||||
c.map(|mut ctx| {
|
Some(ctx) => { ctx.borrow_mut().pop(); }
|
||||||
ctx.pop();
|
None => {}
|
||||||
ctx
|
}
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
|
pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
|
||||||
debug!("new InsnCtxt: {}", s);
|
debug!("new InsnCtxt: {}", s);
|
||||||
local_data::modify(task_local_insn_key, |c| {
|
match task_local_insn_key.get() {
|
||||||
c.map(|mut ctx| {
|
Some(ctx) => ctx.borrow_mut().push(s),
|
||||||
ctx.push(s);
|
None => {}
|
||||||
ctx
|
}
|
||||||
})
|
|
||||||
});
|
|
||||||
_InsnCtxt { _x: () }
|
_InsnCtxt { _x: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,23 +14,21 @@ use syntax::ast;
|
||||||
use syntax::visit;
|
use syntax::visit;
|
||||||
use syntax::visit::Visitor;
|
use syntax::visit::Visitor;
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
pub fn time<T, U>(do_it: bool, what: &str, u: U, f: |U| -> T) -> T {
|
pub fn time<T, U>(do_it: bool, what: &str, u: U, f: |U| -> T) -> T {
|
||||||
local_data_key!(depth: uint);
|
local_data_key!(depth: uint);
|
||||||
if !do_it { return f(u); }
|
if !do_it { return f(u); }
|
||||||
|
|
||||||
let old = local_data::get(depth, |d| d.map(|a| *a).unwrap_or(0));
|
let old = depth.get().map(|d| *d).unwrap_or(0);
|
||||||
local_data::set(depth, old + 1);
|
depth.replace(Some(old + 1));
|
||||||
|
|
||||||
let start = time::precise_time_s();
|
let start = time::precise_time_s();
|
||||||
let rv = f(u);
|
let rv = f(u);
|
||||||
let end = time::precise_time_s();
|
let end = time::precise_time_s();
|
||||||
|
|
||||||
println!("{}time: {:3.3f} s\t{}", " ".repeat(old), end - start, what);
|
println!("{}time: {:3.3f} s\t{}", " ".repeat(old), end - start, what);
|
||||||
local_data::set(depth, old);
|
depth.replace(Some(old));
|
||||||
|
|
||||||
rv
|
rv
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ use rustc::metadata::cstore;
|
||||||
use rustc::metadata::csearch;
|
use rustc::metadata::csearch;
|
||||||
use rustc::metadata::decoder;
|
use rustc::metadata::decoder;
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
|
||||||
use core;
|
use core;
|
||||||
|
@ -77,7 +76,7 @@ pub struct Crate {
|
||||||
|
|
||||||
impl<'a> Clean<Crate> for visit_ast::RustdocVisitor<'a> {
|
impl<'a> Clean<Crate> for visit_ast::RustdocVisitor<'a> {
|
||||||
fn clean(&self) -> Crate {
|
fn clean(&self) -> Crate {
|
||||||
let cx = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let cx = super::ctxtkey.get().unwrap();
|
||||||
|
|
||||||
let mut externs = Vec::new();
|
let mut externs = Vec::new();
|
||||||
cx.sess().cstore.iter_crate_data(|n, meta| {
|
cx.sess().cstore.iter_crate_data(|n, meta| {
|
||||||
|
@ -251,7 +250,7 @@ impl Clean<Item> for doctree::Module {
|
||||||
// determine if we should display the inner contents or
|
// determine if we should display the inner contents or
|
||||||
// the outer `mod` item for the source code.
|
// the outer `mod` item for the source code.
|
||||||
let where = {
|
let where = {
|
||||||
let ctxt = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let ctxt = super::ctxtkey.get().unwrap();
|
||||||
let cm = ctxt.sess().codemap();
|
let cm = ctxt.sess().codemap();
|
||||||
let outer = cm.lookup_char_pos(self.where_outer.lo);
|
let outer = cm.lookup_char_pos(self.where_outer.lo);
|
||||||
let inner = cm.lookup_char_pos(self.where_inner.lo);
|
let inner = cm.lookup_char_pos(self.where_inner.lo);
|
||||||
|
@ -726,7 +725,7 @@ impl Clean<Type> for ast::Ty {
|
||||||
fn clean(&self) -> Type {
|
fn clean(&self) -> Type {
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
debug!("cleaning type `{:?}`", self);
|
debug!("cleaning type `{:?}`", self);
|
||||||
let ctxt = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let ctxt = super::ctxtkey.get().unwrap();
|
||||||
let codemap = ctxt.sess().codemap();
|
let codemap = ctxt.sess().codemap();
|
||||||
debug!("span corresponds to `{}`", codemap.span_to_str(self.span));
|
debug!("span corresponds to `{}`", codemap.span_to_str(self.span));
|
||||||
match self.node {
|
match self.node {
|
||||||
|
@ -909,7 +908,7 @@ pub struct Span {
|
||||||
|
|
||||||
impl Clean<Span> for syntax::codemap::Span {
|
impl Clean<Span> for syntax::codemap::Span {
|
||||||
fn clean(&self) -> Span {
|
fn clean(&self) -> Span {
|
||||||
let ctxt = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let ctxt = super::ctxtkey.get().unwrap();
|
||||||
let cm = ctxt.sess().codemap();
|
let cm = ctxt.sess().codemap();
|
||||||
let filename = cm.span_to_filename(*self);
|
let filename = cm.span_to_filename(*self);
|
||||||
let lo = cm.lookup_char_pos(self.lo);
|
let lo = cm.lookup_char_pos(self.lo);
|
||||||
|
@ -1237,7 +1236,7 @@ trait ToSource {
|
||||||
impl ToSource for syntax::codemap::Span {
|
impl ToSource for syntax::codemap::Span {
|
||||||
fn to_src(&self) -> ~str {
|
fn to_src(&self) -> ~str {
|
||||||
debug!("converting span {:?} to snippet", self.clean());
|
debug!("converting span {:?} to snippet", self.clean());
|
||||||
let ctxt = local_data::get(super::ctxtkey, |x| x.unwrap().clone());
|
let ctxt = super::ctxtkey.get().unwrap();
|
||||||
let cm = ctxt.sess().codemap().clone();
|
let cm = ctxt.sess().codemap().clone();
|
||||||
let sn = match cm.span_to_snippet(*self) {
|
let sn = match cm.span_to_snippet(*self) {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
|
@ -1292,7 +1291,7 @@ fn name_from_pat(p: &ast::Pat) -> ~str {
|
||||||
/// Given a Type, resolve it using the def_map
|
/// Given a Type, resolve it using the def_map
|
||||||
fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound> >,
|
fn resolve_type(path: Path, tpbs: Option<Vec<TyParamBound> >,
|
||||||
id: ast::NodeId) -> Type {
|
id: ast::NodeId) -> Type {
|
||||||
let cx = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let cx = super::ctxtkey.get().unwrap();
|
||||||
let tycx = match cx.maybe_typed {
|
let tycx = match cx.maybe_typed {
|
||||||
core::Typed(ref tycx) => tycx,
|
core::Typed(ref tycx) => tycx,
|
||||||
// If we're extracting tests, this return value doesn't matter.
|
// If we're extracting tests, this return value doesn't matter.
|
||||||
|
@ -1351,7 +1350,7 @@ fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_def(id: ast::NodeId) -> Option<ast::DefId> {
|
fn resolve_def(id: ast::NodeId) -> Option<ast::DefId> {
|
||||||
let cx = local_data::get(super::ctxtkey, |x| *x.unwrap());
|
let cx = super::ctxtkey.get().unwrap();
|
||||||
match cx.maybe_typed {
|
match cx.maybe_typed {
|
||||||
core::Typed(ref tcx) => {
|
core::Typed(ref tcx) => {
|
||||||
tcx.def_map.borrow().find(&id).map(|&d| ast_util::def_id_of_def(d))
|
tcx.def_map.borrow().find(&id).map(|&d| ast_util::def_id_of_def(d))
|
||||||
|
|
|
@ -20,7 +20,6 @@ use syntax;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::local_data;
|
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
|
|
||||||
use visit_ast::RustdocVisitor;
|
use visit_ast::RustdocVisitor;
|
||||||
|
@ -109,7 +108,7 @@ pub fn run_core(libs: HashSet<Path>, cfgs: Vec<~str>, path: &Path)
|
||||||
-> (clean::Crate, CrateAnalysis) {
|
-> (clean::Crate, CrateAnalysis) {
|
||||||
let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs);
|
let (ctxt, analysis) = get_ast_and_resolve(path, libs, cfgs);
|
||||||
let ctxt = @ctxt;
|
let ctxt = @ctxt;
|
||||||
local_data::set(super::ctxtkey, ctxt);
|
super::ctxtkey.replace(Some(ctxt));
|
||||||
|
|
||||||
let krate = {
|
let krate = {
|
||||||
let mut v = RustdocVisitor::new(ctxt, Some(&analysis));
|
let mut v = RustdocVisitor::new(ctxt, Some(&analysis));
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::local_data;
|
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
@ -206,78 +205,72 @@ fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
|
||||||
generics.push_str(">");
|
generics.push_str(">");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did someone say rightward-drift?
|
let loc = current_location_key.get().unwrap();
|
||||||
local_data::get(current_location_key, |loc| {
|
let cache = cache_key.get().unwrap();
|
||||||
let loc = loc.unwrap();
|
let abs_root = root(&**cache, loc.as_slice());
|
||||||
|
let rel_root = match path.segments.get(0).name.as_slice() {
|
||||||
|
"self" => Some("./".to_owned()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
local_data::get(cache_key, |cache| {
|
if print_all {
|
||||||
let cache = cache.unwrap();
|
let amt = path.segments.len() - 1;
|
||||||
let abs_root = root(&**cache, loc.as_slice());
|
match rel_root {
|
||||||
let rel_root = match path.segments.get(0).name.as_slice() {
|
Some(root) => {
|
||||||
"self" => Some("./".to_owned()),
|
let mut root = StrBuf::from_str(root);
|
||||||
_ => None,
|
for seg in path.segments.slice_to(amt).iter() {
|
||||||
};
|
if "super" == seg.name || "self" == seg.name {
|
||||||
|
try!(write!(w, "{}::", seg.name));
|
||||||
if print_all {
|
} else {
|
||||||
let amt = path.segments.len() - 1;
|
root.push_str(seg.name);
|
||||||
match rel_root {
|
root.push_str("/");
|
||||||
Some(root) => {
|
try!(write!(w, "<a class='mod'
|
||||||
let mut root = StrBuf::from_str(root);
|
href='{}index.html'>{}</a>::",
|
||||||
for seg in path.segments.slice_to(amt).iter() {
|
root.as_slice(),
|
||||||
if "super" == seg.name || "self" == seg.name {
|
seg.name));
|
||||||
try!(write!(w, "{}::", seg.name));
|
|
||||||
} else {
|
|
||||||
root.push_str(seg.name);
|
|
||||||
root.push_str("/");
|
|
||||||
try!(write!(w, "<a class='mod'
|
|
||||||
href='{}index.html'>{}</a>::",
|
|
||||||
root.as_slice(),
|
|
||||||
seg.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
for seg in path.segments.slice_to(amt).iter() {
|
|
||||||
try!(write!(w, "{}::", seg.name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
match info(&**cache) {
|
for seg in path.segments.slice_to(amt).iter() {
|
||||||
// This is a documented path, link to it!
|
try!(write!(w, "{}::", seg.name));
|
||||||
Some((ref fqp, shortty)) if abs_root.is_some() => {
|
|
||||||
let mut url = StrBuf::from_str(abs_root.unwrap());
|
|
||||||
let to_link = fqp.slice_to(fqp.len() - 1);
|
|
||||||
for component in to_link.iter() {
|
|
||||||
url.push_str(*component);
|
|
||||||
url.push_str("/");
|
|
||||||
}
|
|
||||||
match shortty {
|
|
||||||
item_type::Module => {
|
|
||||||
url.push_str(*fqp.last().unwrap());
|
|
||||||
url.push_str("/index.html");
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
url.push_str(shortty.to_static_str());
|
|
||||||
url.push_str(".");
|
|
||||||
url.push_str(*fqp.last().unwrap());
|
|
||||||
url.push_str(".html");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
|
|
||||||
shortty, url, fqp.connect("::"), last.name));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match info(&**cache) {
|
||||||
|
// This is a documented path, link to it!
|
||||||
|
Some((ref fqp, shortty)) if abs_root.is_some() => {
|
||||||
|
let mut url = StrBuf::from_str(abs_root.unwrap());
|
||||||
|
let to_link = fqp.slice_to(fqp.len() - 1);
|
||||||
|
for component in to_link.iter() {
|
||||||
|
url.push_str(*component);
|
||||||
|
url.push_str("/");
|
||||||
|
}
|
||||||
|
match shortty {
|
||||||
|
item_type::Module => {
|
||||||
|
url.push_str(*fqp.last().unwrap());
|
||||||
|
url.push_str("/index.html");
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
try!(write!(w, "{}", last.name));
|
url.push_str(shortty.to_static_str());
|
||||||
|
url.push_str(".");
|
||||||
|
url.push_str(*fqp.last().unwrap());
|
||||||
|
url.push_str(".html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(write!(w, "{}", generics.as_slice()));
|
|
||||||
Ok(())
|
try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
|
||||||
})
|
shortty, url, fqp.connect("::"), last.name));
|
||||||
})
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
try!(write!(w, "{}", last.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try!(write!(w, "{}", generics.as_slice()));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to render type parameters
|
/// Helper to render type parameters
|
||||||
|
@ -302,10 +295,8 @@ impl fmt::Show for clean::Type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
clean::TyParamBinder(id) | clean::Generic(id) => {
|
clean::TyParamBinder(id) | clean::Generic(id) => {
|
||||||
local_data::get(cache_key, |cache| {
|
let m = cache_key.get().unwrap();
|
||||||
let m = cache.unwrap();
|
f.buf.write(m.typarams.get(&id).as_bytes())
|
||||||
f.buf.write(m.typarams.get(&id).as_bytes())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
|
clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
|
||||||
try!(resolved_path(f.buf, id, path, false));
|
try!(resolved_path(f.buf, id, path, false));
|
||||||
|
|
|
@ -27,11 +27,11 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::local_data;
|
|
||||||
use std::str;
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use std::str;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
|
||||||
use html::toc::TocBuilder;
|
use html::toc::TocBuilder;
|
||||||
|
@ -139,7 +139,7 @@ fn stripped_filtered_line<'a>(s: &'a str) -> Option<&'a str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data_key!(used_header_map: HashMap<~str, uint>)
|
local_data_key!(used_header_map: RefCell<HashMap<~str, uint>>)
|
||||||
|
|
||||||
pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result {
|
pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result {
|
||||||
extern fn block(ob: *mut hoedown_buffer, text: *hoedown_buffer,
|
extern fn block(ob: *mut hoedown_buffer, text: *hoedown_buffer,
|
||||||
|
@ -216,15 +216,12 @@ pub fn render(w: &mut io::Writer, s: &str, print_toc: bool) -> fmt::Result {
|
||||||
let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
|
let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) };
|
||||||
|
|
||||||
// Make sure our hyphenated ID is unique for this page
|
// Make sure our hyphenated ID is unique for this page
|
||||||
let id = local_data::get_mut(used_header_map, |map| {
|
let map = used_header_map.get().unwrap();
|
||||||
let map = map.unwrap();
|
let id = match map.borrow_mut().find_mut(&id) {
|
||||||
match map.find_mut(&id) {
|
None => id,
|
||||||
None => {}
|
Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) }
|
||||||
Some(a) => { *a += 1; return format!("{}-{}", id, *a - 1) }
|
};
|
||||||
}
|
map.borrow_mut().insert(id.clone(), 1);
|
||||||
map.insert(id.clone(), 1);
|
|
||||||
id.clone()
|
|
||||||
});
|
|
||||||
|
|
||||||
let sec = match opaque.toc_builder {
|
let sec = match opaque.toc_builder {
|
||||||
Some(ref mut builder) => {
|
Some(ref mut builder) => {
|
||||||
|
@ -348,7 +345,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
|
||||||
/// used at the beginning of rendering an entire HTML page to reset from the
|
/// used at the beginning of rendering an entire HTML page to reset from the
|
||||||
/// previous state (if any).
|
/// previous state (if any).
|
||||||
pub fn reset_headers() {
|
pub fn reset_headers() {
|
||||||
local_data::set(used_header_map, HashMap::new())
|
used_header_map.replace(Some(RefCell::new(HashMap::new())));
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Show for Markdown<'a> {
|
impl<'a> fmt::Show for Markdown<'a> {
|
||||||
|
|
|
@ -37,7 +37,6 @@ use collections::{HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{fs, File, BufferedWriter, MemWriter, BufferedReader};
|
use std::io::{fs, File, BufferedWriter, MemWriter, BufferedReader};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::local_data;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
|
||||||
|
@ -243,24 +242,22 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crawl the crate to build various caches used for the output
|
// Crawl the crate to build various caches used for the output
|
||||||
let mut cache = local_data::get(::analysiskey, |analysis| {
|
let public_items = ::analysiskey.get().map(|a| a.public_items.clone());
|
||||||
let public_items = analysis.map(|a| a.public_items.clone());
|
let public_items = public_items.unwrap_or(NodeSet::new());
|
||||||
let public_items = public_items.unwrap_or(NodeSet::new());
|
let mut cache = Cache {
|
||||||
Cache {
|
impls: HashMap::new(),
|
||||||
impls: HashMap::new(),
|
typarams: HashMap::new(),
|
||||||
typarams: HashMap::new(),
|
paths: HashMap::new(),
|
||||||
paths: HashMap::new(),
|
traits: HashMap::new(),
|
||||||
traits: HashMap::new(),
|
implementors: HashMap::new(),
|
||||||
implementors: HashMap::new(),
|
stack: Vec::new(),
|
||||||
stack: Vec::new(),
|
parent_stack: Vec::new(),
|
||||||
parent_stack: Vec::new(),
|
search_index: Vec::new(),
|
||||||
search_index: Vec::new(),
|
extern_locations: HashMap::new(),
|
||||||
extern_locations: HashMap::new(),
|
privmod: false,
|
||||||
privmod: false,
|
public_items: public_items,
|
||||||
public_items: public_items,
|
orphan_methods: Vec::new(),
|
||||||
orphan_methods: Vec::new(),
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
cache.stack.push(krate.name.clone());
|
cache.stack.push(krate.name.clone());
|
||||||
krate = cache.fold_crate(krate);
|
krate = cache.fold_crate(krate);
|
||||||
|
|
||||||
|
@ -833,7 +830,7 @@ impl Context {
|
||||||
item.name = Some(krate.name);
|
item.name = Some(krate.name);
|
||||||
|
|
||||||
// using a rwarc makes this parallelizable in the future
|
// using a rwarc makes this parallelizable in the future
|
||||||
local_data::set(cache_key, Arc::new(cache));
|
cache_key.replace(Some(Arc::new(cache)));
|
||||||
|
|
||||||
let mut work = vec!((self, item));
|
let mut work = vec!((self, item));
|
||||||
loop {
|
loop {
|
||||||
|
@ -859,7 +856,7 @@ impl Context {
|
||||||
info!("Rendering an item to {}", w.path().display());
|
info!("Rendering an item to {}", w.path().display());
|
||||||
// A little unfortunate that this is done like this, but it sure
|
// A little unfortunate that this is done like this, but it sure
|
||||||
// does make formatting *a lot* nicer.
|
// does make formatting *a lot* nicer.
|
||||||
local_data::set(current_location_key, cx.current.clone());
|
current_location_key.replace(Some(cx.current.clone()));
|
||||||
|
|
||||||
let mut title = StrBuf::from_str(cx.current.connect("::"));
|
let mut title = StrBuf::from_str(cx.current.connect("::"));
|
||||||
if pushname {
|
if pushname {
|
||||||
|
@ -1299,31 +1296,28 @@ fn item_trait(w: &mut Writer, it: &clean::Item,
|
||||||
try!(write!(w, "</div>"));
|
try!(write!(w, "</div>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
local_data::get(cache_key, |cache| {
|
match cache_key.get().unwrap().implementors.find(&it.id) {
|
||||||
let cache = cache.unwrap();
|
Some(implementors) => {
|
||||||
match cache.implementors.find(&it.id) {
|
try!(write!(w, "
|
||||||
Some(implementors) => {
|
<h2 id='implementors'>Implementors</h2>
|
||||||
try!(write!(w, "
|
<ul class='item-list'>
|
||||||
<h2 id='implementors'>Implementors</h2>
|
"));
|
||||||
<ul class='item-list'>
|
for i in implementors.iter() {
|
||||||
"));
|
match *i {
|
||||||
for i in implementors.iter() {
|
PathType(ref ty) => {
|
||||||
match *i {
|
try!(write!(w, "<li><code>{}</code></li>", *ty));
|
||||||
PathType(ref ty) => {
|
}
|
||||||
try!(write!(w, "<li><code>{}</code></li>", *ty));
|
OtherType(ref generics, ref trait_, ref for_) => {
|
||||||
}
|
try!(write!(w, "<li><code>impl{} {} for {}</code></li>",
|
||||||
OtherType(ref generics, ref trait_, ref for_) => {
|
*generics, *trait_, *for_));
|
||||||
try!(write!(w, "<li><code>impl{} {} for {}</code></li>",
|
|
||||||
*generics, *trait_, *for_));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(write!(w, "</ul>"));
|
|
||||||
}
|
}
|
||||||
None => {}
|
try!(write!(w, "</ul>"));
|
||||||
}
|
}
|
||||||
Ok(())
|
None => {}
|
||||||
})
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_method(w: &mut Writer, meth: &clean::Item) -> fmt::Result {
|
fn render_method(w: &mut Writer, meth: &clean::Item) -> fmt::Result {
|
||||||
|
@ -1550,51 +1544,48 @@ fn render_struct(w: &mut Writer, it: &clean::Item,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_methods(w: &mut Writer, it: &clean::Item) -> fmt::Result {
|
fn render_methods(w: &mut Writer, it: &clean::Item) -> fmt::Result {
|
||||||
local_data::get(cache_key, |cache| {
|
match cache_key.get().unwrap().impls.find(&it.id) {
|
||||||
let c = cache.unwrap();
|
Some(v) => {
|
||||||
match c.impls.find(&it.id) {
|
let mut non_trait = v.iter().filter(|p| {
|
||||||
Some(v) => {
|
p.ref0().trait_.is_none()
|
||||||
let mut non_trait = v.iter().filter(|p| {
|
});
|
||||||
p.ref0().trait_.is_none()
|
let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<~str>)>>();
|
||||||
});
|
let mut traits = v.iter().filter(|p| {
|
||||||
let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<~str>)>>();
|
p.ref0().trait_.is_some()
|
||||||
let mut traits = v.iter().filter(|p| {
|
});
|
||||||
p.ref0().trait_.is_some()
|
let traits = traits.collect::<Vec<&(clean::Impl, Option<~str>)>>();
|
||||||
});
|
|
||||||
let traits = traits.collect::<Vec<&(clean::Impl, Option<~str>)>>();
|
|
||||||
|
|
||||||
if non_trait.len() > 0 {
|
if non_trait.len() > 0 {
|
||||||
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
||||||
for &(ref i, ref dox) in non_trait.move_iter() {
|
for &(ref i, ref dox) in non_trait.move_iter() {
|
||||||
|
try!(render_impl(w, i, dox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if traits.len() > 0 {
|
||||||
|
try!(write!(w, "<h2 id='implementations'>Trait \
|
||||||
|
Implementations</h2>"));
|
||||||
|
let mut any_derived = false;
|
||||||
|
for & &(ref i, ref dox) in traits.iter() {
|
||||||
|
if !i.derived {
|
||||||
try!(render_impl(w, i, dox));
|
try!(render_impl(w, i, dox));
|
||||||
|
} else {
|
||||||
|
any_derived = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if traits.len() > 0 {
|
if any_derived {
|
||||||
try!(write!(w, "<h2 id='implementations'>Trait \
|
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
|
||||||
Implementations</h2>"));
|
</h3>"));
|
||||||
let mut any_derived = false;
|
for &(ref i, ref dox) in traits.move_iter() {
|
||||||
for & &(ref i, ref dox) in traits.iter() {
|
if i.derived {
|
||||||
if !i.derived {
|
|
||||||
try!(render_impl(w, i, dox));
|
try!(render_impl(w, i, dox));
|
||||||
} else {
|
|
||||||
any_derived = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if any_derived {
|
|
||||||
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
|
|
||||||
</h3>"));
|
|
||||||
for &(ref i, ref dox) in traits.move_iter() {
|
|
||||||
if i.derived {
|
|
||||||
try!(render_impl(w, i, dox));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
None => {}
|
||||||
})
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_impl(w: &mut Writer, i: &clean::Impl,
|
fn render_impl(w: &mut Writer, i: &clean::Impl,
|
||||||
|
@ -1644,9 +1635,8 @@ fn render_impl(w: &mut Writer, i: &clean::Impl,
|
||||||
match trait_id {
|
match trait_id {
|
||||||
None => {}
|
None => {}
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
try!(local_data::get(cache_key, |cache| {
|
try!({
|
||||||
let cache = cache.unwrap();
|
match cache_key.get().unwrap().traits.find(&id) {
|
||||||
match cache.traits.find(&id) {
|
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
for method in t.methods.iter() {
|
for method in t.methods.iter() {
|
||||||
let n = method.item().name.clone();
|
let n = method.item().name.clone();
|
||||||
|
@ -1661,7 +1651,7 @@ fn render_impl(w: &mut Writer, i: &clean::Impl,
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(write!(w, "</div>"));
|
try!(write!(w, "</div>"));
|
||||||
|
|
|
@ -28,7 +28,6 @@ extern crate time;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{File, MemWriter};
|
use std::io::{File, MemWriter};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
@ -276,7 +275,7 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output {
|
||||||
&cr)
|
&cr)
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
info!("finished with rustc");
|
info!("finished with rustc");
|
||||||
local_data::set(analysiskey, analysis);
|
analysiskey.replace(Some(analysis));
|
||||||
|
|
||||||
// Process all of the crate attributes, extracting plugin metadata along
|
// Process all of the crate attributes, extracting plugin metadata along
|
||||||
// with the passes which we are supposed to run.
|
// with the passes which we are supposed to run.
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use rustc::util::nodemap::NodeSet;
|
use rustc::util::nodemap::NodeSet;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::local_data;
|
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
use std::uint;
|
use std::uint;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
@ -86,13 +85,11 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
|
||||||
|
|
||||||
/// Strip private items from the point of view of a crate or externally from a
|
/// Strip private items from the point of view of a crate or externally from a
|
||||||
/// crate, specified by the `xcrate` flag.
|
/// crate, specified by the `xcrate` flag.
|
||||||
pub fn strip_private(krate: clean::Crate) -> plugins::PluginResult {
|
pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
|
||||||
// This stripper collects all *retained* nodes.
|
// This stripper collects all *retained* nodes.
|
||||||
let mut retained = HashSet::new();
|
let mut retained = HashSet::new();
|
||||||
let exported_items = local_data::get(super::analysiskey, |analysis| {
|
let analysis = super::analysiskey.get().unwrap();
|
||||||
analysis.unwrap().exported_items.clone()
|
let exported_items = analysis.exported_items.clone();
|
||||||
});
|
|
||||||
let mut krate = krate;
|
|
||||||
|
|
||||||
// strip all private items
|
// strip all private items
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,6 @@ use std::cell::RefCell;
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Process, TempDir};
|
use std::io::{Process, TempDir};
|
||||||
use std::local_data;
|
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
@ -75,7 +74,7 @@ pub fn run(input: &str,
|
||||||
maybe_typed: core::NotTyped(sess),
|
maybe_typed: core::NotTyped(sess),
|
||||||
src: input_path,
|
src: input_path,
|
||||||
};
|
};
|
||||||
local_data::set(super::ctxtkey, ctx);
|
super::ctxtkey.replace(Some(ctx));
|
||||||
|
|
||||||
let mut v = RustdocVisitor::new(ctx, None);
|
let mut v = RustdocVisitor::new(ctx, None);
|
||||||
v.visit(&ctx.krate);
|
v.visit(&ctx.krate);
|
||||||
|
|
|
@ -23,16 +23,14 @@ named and annotated. This name is then passed to the functions in this module to
|
||||||
modify/read the slot specified by the key.
|
modify/read the slot specified by the key.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::local_data;
|
|
||||||
|
|
||||||
local_data_key!(key_int: int)
|
local_data_key!(key_int: int)
|
||||||
local_data_key!(key_vector: ~[int])
|
local_data_key!(key_vector: ~[int])
|
||||||
|
|
||||||
local_data::set(key_int, 3);
|
key_int.replace(Some(3));
|
||||||
local_data::get(key_int, |opt| assert_eq!(opt.map(|x| *x), Some(3)));
|
assert_eq!(*key_int.get().unwrap(), 3);
|
||||||
|
|
||||||
local_data::set(key_vector, ~[4]);
|
key_vector.replace(Some(~[4]));
|
||||||
local_data::get(key_vector, |opt| assert_eq!(*opt.unwrap(), ~[4]));
|
assert_eq!(*key_vector.get().unwrap(), ~[4]);
|
||||||
```
|
```
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -43,9 +41,12 @@ local_data::get(key_vector, |opt| assert_eq!(*opt.unwrap(), ~[4]));
|
||||||
use cast;
|
use cast;
|
||||||
use iter::{Iterator};
|
use iter::{Iterator};
|
||||||
use kinds::Send;
|
use kinds::Send;
|
||||||
|
use kinds::marker;
|
||||||
use mem::replace;
|
use mem::replace;
|
||||||
|
use ops::{Drop, Deref};
|
||||||
use option::{None, Option, Some};
|
use option::{None, Option, Some};
|
||||||
use owned::Box;
|
use owned::Box;
|
||||||
|
use raw;
|
||||||
use rt::task::{Task, LocalStorage};
|
use rt::task::{Task, LocalStorage};
|
||||||
use slice::{ImmutableVector, MutableVector};
|
use slice::{ImmutableVector, MutableVector};
|
||||||
use vec::Vec;
|
use vec::Vec;
|
||||||
|
@ -66,7 +67,7 @@ pub type Key<T> = &'static KeyValue<T>;
|
||||||
#[allow(missing_doc)]
|
#[allow(missing_doc)]
|
||||||
pub enum KeyValue<T> { Key }
|
pub enum KeyValue<T> { Key }
|
||||||
|
|
||||||
#[allow(missing_doc)]
|
#[doc(hidden)]
|
||||||
trait LocalData {}
|
trait LocalData {}
|
||||||
impl<T: 'static> LocalData for T {}
|
impl<T: 'static> LocalData for T {}
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ impl<T: 'static> LocalData for T {}
|
||||||
// n.b. If TLS is used heavily in future, this could be made more efficient with
|
// n.b. If TLS is used heavily in future, this could be made more efficient with
|
||||||
// a proper map.
|
// a proper map.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub type Map = Vec<Option<(*u8, TLSValue, LoanState)>>;
|
pub type Map = Vec<Option<(*u8, TLSValue, uint)>>;
|
||||||
type TLSValue = Box<LocalData:Send>;
|
type TLSValue = Box<LocalData:Send>;
|
||||||
|
|
||||||
// Gets the map from the runtime. Lazily initialises if not done so already.
|
// Gets the map from the runtime. Lazily initialises if not done so already.
|
||||||
|
@ -111,243 +112,156 @@ unsafe fn get_local_map() -> &mut Map {
|
||||||
*slot = Some(vec!());
|
*slot = Some(vec!());
|
||||||
match *slot {
|
match *slot {
|
||||||
Some(ref mut map_ptr) => { return map_ptr }
|
Some(ref mut map_ptr) => { return map_ptr }
|
||||||
None => abort()
|
None => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq)]
|
|
||||||
enum LoanState {
|
|
||||||
NoLoan, ImmLoan, MutLoan
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoanState {
|
|
||||||
fn describe(&self) -> &'static str {
|
|
||||||
match *self {
|
|
||||||
NoLoan => "no loan",
|
|
||||||
ImmLoan => "immutable",
|
|
||||||
MutLoan => "mutable"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_to_key_value<T: 'static>(key: Key<T>) -> *u8 {
|
fn key_to_key_value<T: 'static>(key: Key<T>) -> *u8 {
|
||||||
unsafe { cast::transmute(key) }
|
key as *KeyValue<T> as *u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a task-local value from task-local storage. This will return
|
/// An RAII immutable reference to a task-local value.
|
||||||
/// Some(value) if the key was present in TLS, otherwise it will return None.
|
|
||||||
///
|
///
|
||||||
/// A runtime assertion will be triggered it removal of TLS value is attempted
|
/// The task-local data can be accessed through this value, and when this
|
||||||
/// while the value is still loaned out via `get` or `get_mut`.
|
/// structure is dropped it will return the borrow on the data.
|
||||||
pub fn pop<T: 'static>(key: Key<T>) -> Option<T> {
|
pub struct Ref<T> {
|
||||||
let map = unsafe { get_local_map() };
|
ptr: &'static T,
|
||||||
let key_value = key_to_key_value(key);
|
key: Key<T>,
|
||||||
|
index: uint,
|
||||||
for entry in map.mut_iter() {
|
nosend: marker::NoSend,
|
||||||
match *entry {
|
|
||||||
Some((k, _, loan)) if k == key_value => {
|
|
||||||
if loan != NoLoan {
|
|
||||||
fail!("TLS value cannot be removed because it is currently \
|
|
||||||
borrowed as {}", loan.describe());
|
|
||||||
}
|
|
||||||
// Move the data out of the `entry` slot via prelude::replace.
|
|
||||||
// This is guaranteed to succeed because we already matched
|
|
||||||
// on `Some` above.
|
|
||||||
let data = match replace(entry, None) {
|
|
||||||
Some((_, data, _)) => data,
|
|
||||||
None => abort()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Move `data` into transmute to get out the memory that it
|
|
||||||
// owns, we must free it manually later.
|
|
||||||
let (_vtable, alloc): (uint, Box<T>) = unsafe {
|
|
||||||
cast::transmute(data)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now that we own `alloc`, we can just move out of it as we
|
|
||||||
// would with any other data.
|
|
||||||
return Some(*alloc);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a value from TLS. The closure provided is yielded `Some` of a
|
impl<T: 'static> KeyValue<T> {
|
||||||
/// reference to the value located in TLS if one exists, or `None` if the key
|
/// Replaces a value in task local storage.
|
||||||
/// provided is not present in TLS currently.
|
///
|
||||||
///
|
/// If this key is already present in TLS, then the previous value is
|
||||||
/// It is considered a runtime error to attempt to get a value which is already
|
/// replaced with the provided data, and then returned.
|
||||||
/// on loan via the `get_mut` method provided.
|
///
|
||||||
pub fn get<T: 'static, U>(key: Key<T>, f: |Option<&T>| -> U) -> U {
|
/// # Failure
|
||||||
get_with(key, ImmLoan, f)
|
///
|
||||||
}
|
/// This function will fail if this key is present in TLS and currently on
|
||||||
|
/// loan with the `get` method.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// local_data_key!(foo: int)
|
||||||
|
///
|
||||||
|
/// assert_eq!(foo.replace(Some(10)), None);
|
||||||
|
/// assert_eq!(foo.replace(Some(4)), Some(10));
|
||||||
|
/// assert_eq!(foo.replace(None), Some(4));
|
||||||
|
/// ```
|
||||||
|
pub fn replace(&'static self, data: Option<T>) -> Option<T> {
|
||||||
|
let map = unsafe { get_local_map() };
|
||||||
|
let keyval = key_to_key_value(self);
|
||||||
|
|
||||||
/// Retrieves a mutable value from TLS. The closure provided is yielded `Some`
|
// When the task-local map is destroyed, all the data needs to be
|
||||||
/// of a reference to the mutable value located in TLS if one exists, or `None`
|
// cleaned up. For this reason we can't do some clever tricks to store
|
||||||
/// if the key provided is not present in TLS currently.
|
// '~T' as a '*c_void' or something like that. To solve the problem, we
|
||||||
///
|
// cast everything to a trait (LocalData) which is then stored inside
|
||||||
/// It is considered a runtime error to attempt to get a value which is already
|
// the map. Upon destruction of the map, all the objects will be
|
||||||
/// on loan via this or the `get` methods.
|
// destroyed and the traits have enough information about them to
|
||||||
pub fn get_mut<T: 'static, U>(key: Key<T>, f: |Option<&mut T>| -> U) -> U {
|
// destroy themselves.
|
||||||
get_with(key, MutLoan, |x| {
|
//
|
||||||
match x {
|
// Additionally, the type of the local data map must ascribe to Send, so
|
||||||
None => f(None),
|
// we do the transmute here to add the Send bound back on. This doesn't
|
||||||
// We're violating a lot of compiler guarantees with this
|
// actually matter because TLS will always own the data (until its moved
|
||||||
// invocation of `transmute`, but we're doing runtime checks to
|
// out) and we're not actually sending it to other schedulers or
|
||||||
// ensure that it's always valid (only one at a time).
|
// anything.
|
||||||
//
|
let newval = data.map(|d| {
|
||||||
// there is no need to be upset!
|
let d = box d as Box<LocalData>;
|
||||||
Some(x) => { f(Some(unsafe { cast::transmute::<&_, &mut _>(x) })) }
|
let d: Box<LocalData:Send> = unsafe { cast::transmute(d) };
|
||||||
}
|
(keyval, d, 0)
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_with<T:'static,
|
|
||||||
U>(
|
|
||||||
key: Key<T>,
|
|
||||||
state: LoanState,
|
|
||||||
f: |Option<&T>| -> U)
|
|
||||||
-> U {
|
|
||||||
// This function must be extremely careful. Because TLS can store owned
|
|
||||||
// values, and we must have some form of `get` function other than `pop`,
|
|
||||||
// this function has to give a `&` reference back to the caller.
|
|
||||||
//
|
|
||||||
// One option is to return the reference, but this cannot be sound because
|
|
||||||
// the actual lifetime of the object is not known. The slot in TLS could not
|
|
||||||
// be modified until the object goes out of scope, but the TLS code cannot
|
|
||||||
// know when this happens.
|
|
||||||
//
|
|
||||||
// For this reason, the reference is yielded to a specified closure. This
|
|
||||||
// way the TLS code knows exactly what the lifetime of the yielded pointer
|
|
||||||
// is, allowing callers to acquire references to owned data. This is also
|
|
||||||
// sound so long as measures are taken to ensure that while a TLS slot is
|
|
||||||
// loaned out to a caller, it's not modified recursively.
|
|
||||||
let map = unsafe { get_local_map() };
|
|
||||||
let key_value = key_to_key_value(key);
|
|
||||||
|
|
||||||
let pos = map.iter().position(|entry| {
|
|
||||||
match *entry {
|
|
||||||
Some((k, _, _)) if k == key_value => true, _ => false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
match pos {
|
|
||||||
None => { return f(None); }
|
|
||||||
Some(i) => {
|
|
||||||
let ret;
|
|
||||||
let mut return_loan = false;
|
|
||||||
match *map.get_mut(i) {
|
|
||||||
Some((_, ref data, ref mut loan)) => {
|
|
||||||
match (state, *loan) {
|
|
||||||
(_, NoLoan) => {
|
|
||||||
*loan = state;
|
|
||||||
return_loan = true;
|
|
||||||
}
|
|
||||||
(ImmLoan, ImmLoan) => {}
|
|
||||||
(want, cur) => {
|
|
||||||
fail!("TLS slot cannot be borrowed as {} because \
|
|
||||||
it is already borrowed as {}",
|
|
||||||
want.describe(), cur.describe());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// data was created with `box T as Box<LocalData>`, so we
|
|
||||||
// extract pointer part of the trait, (as Box<T>), and
|
|
||||||
// then use compiler coercions to achieve a '&' pointer.
|
|
||||||
unsafe {
|
|
||||||
match *cast::transmute::<&TLSValue,
|
|
||||||
&(uint, Box<T>)>(data){
|
|
||||||
(_vtable, ref alloc) => {
|
|
||||||
let value: &T = *alloc;
|
|
||||||
ret = f(Some(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
// n.b. 'data' and 'loans' are both invalid pointers at the point
|
|
||||||
// 'f' returned because `f` could have appended more TLS items which
|
|
||||||
// in turn relocated the vector. Hence we do another lookup here to
|
|
||||||
// fixup the loans.
|
|
||||||
if return_loan {
|
|
||||||
match *map.get_mut(i) {
|
|
||||||
Some((_, _, ref mut loan)) => { *loan = NoLoan; }
|
|
||||||
None => abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn abort() -> ! {
|
|
||||||
use intrinsics;
|
|
||||||
unsafe { intrinsics::abort() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts a value into task local storage. If the key is already present in
|
|
||||||
/// TLS, then the previous value is removed and replaced with the provided data.
|
|
||||||
///
|
|
||||||
/// It is considered a runtime error to attempt to set a key which is currently
|
|
||||||
/// on loan via the `get` or `get_mut` methods.
|
|
||||||
pub fn set<T: 'static>(key: Key<T>, data: T) {
|
|
||||||
let map = unsafe { get_local_map() };
|
|
||||||
let keyval = key_to_key_value(key);
|
|
||||||
|
|
||||||
// When the task-local map is destroyed, all the data needs to be cleaned
|
|
||||||
// up. For this reason we can't do some clever tricks to store 'Box<T>' as
|
|
||||||
// a '*c_void' or something like that. To solve the problem, we cast
|
|
||||||
// everything to a trait (LocalData) which is then stored inside the map.
|
|
||||||
// Upon destruction of the map, all the objects will be destroyed and the
|
|
||||||
// traits have enough information about them to destroy themselves.
|
|
||||||
let data = box data as Box<LocalData:>;
|
|
||||||
|
|
||||||
fn insertion_position(map: &mut Map,
|
|
||||||
key: *u8) -> Option<uint> {
|
|
||||||
// First see if the map contains this key already
|
|
||||||
let curspot = map.iter().position(|entry| {
|
|
||||||
match *entry {
|
|
||||||
Some((ekey, _, loan)) if key == ekey => {
|
|
||||||
if loan != NoLoan {
|
|
||||||
fail!("TLS value cannot be overwritten because it is
|
|
||||||
already borrowed as {}", loan.describe())
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// If it doesn't contain the key, just find a slot that's None
|
|
||||||
match curspot {
|
let pos = match self.find(map) {
|
||||||
Some(i) => Some(i),
|
Some((i, _, &0)) => Some(i),
|
||||||
None => map.iter().position(|entry| entry.is_none())
|
Some((_, _, _)) => fail!("TLS value cannot be replaced because it \
|
||||||
|
is already borrowed"),
|
||||||
|
None => map.iter().position(|entry| entry.is_none()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match pos {
|
||||||
|
Some(i) => {
|
||||||
|
replace(map.get_mut(i), newval).map(|(_, data, _)| {
|
||||||
|
// Move `data` into transmute to get out the memory that it
|
||||||
|
// owns, we must free it manually later.
|
||||||
|
let t: raw::TraitObject = unsafe { cast::transmute(data) };
|
||||||
|
let alloc: Box<T> = unsafe { cast::transmute(t.data) };
|
||||||
|
|
||||||
|
// Now that we own `alloc`, we can just move out of it as we
|
||||||
|
// would with any other data.
|
||||||
|
*alloc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
map.push(newval);
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type of the local data map must ascribe to Send, so we do the
|
/// Borrows a value from TLS.
|
||||||
// transmute here to add the Send bound back on. This doesn't actually
|
///
|
||||||
// matter because TLS will always own the data (until its moved out) and
|
/// If `None` is returned, then this key is not present in TLS. If `Some` is
|
||||||
// we're not actually sending it to other schedulers or anything.
|
/// returned, then the returned data is a smart pointer representing a new
|
||||||
let data: Box<LocalData:Send> = unsafe { cast::transmute(data) };
|
/// loan on this TLS key. While on loan, this key cannot be altered via the
|
||||||
match insertion_position(map, keyval) {
|
/// `replace` method.
|
||||||
Some(i) => { *map.get_mut(i) = Some((keyval, data, NoLoan)); }
|
///
|
||||||
None => { map.push(Some((keyval, data, NoLoan))); }
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// local_data_key!(key: int)
|
||||||
|
///
|
||||||
|
/// assert!(key.get().is_none());
|
||||||
|
///
|
||||||
|
/// key.replace(Some(3));
|
||||||
|
/// assert_eq!(*key.get().unwrap(), 3);
|
||||||
|
/// ```
|
||||||
|
pub fn get(&'static self) -> Option<Ref<T>> {
|
||||||
|
let map = unsafe { get_local_map() };
|
||||||
|
|
||||||
|
self.find(map).map(|(pos, data, loan)| {
|
||||||
|
*loan += 1;
|
||||||
|
|
||||||
|
// data was created with `~T as ~LocalData`, so we extract
|
||||||
|
// pointer part of the trait, (as ~T), and then use
|
||||||
|
// compiler coercions to achieve a '&' pointer.
|
||||||
|
let ptr = unsafe {
|
||||||
|
let data = data as *Box<LocalData:Send> as *raw::TraitObject;
|
||||||
|
&mut *((*data).data as *mut T)
|
||||||
|
};
|
||||||
|
Ref { ptr: ptr, index: pos, nosend: marker::NoSend, key: self }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find<'a>(&'static self,
|
||||||
|
map: &'a mut Map) -> Option<(uint, &'a TLSValue, &'a mut uint)>{
|
||||||
|
let key_value = key_to_key_value(self);
|
||||||
|
map.mut_iter().enumerate().filter_map(|(i, entry)| {
|
||||||
|
match *entry {
|
||||||
|
Some((k, ref data, ref mut loan)) if k == key_value => {
|
||||||
|
Some((i, data, loan))
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}).next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies a task-local value by temporarily removing it from task-local
|
impl<T: 'static> Deref<T> for Ref<T> {
|
||||||
/// storage and then re-inserting if `Some` is returned from the closure.
|
fn deref<'a>(&'a self) -> &'a T { self.ptr }
|
||||||
///
|
}
|
||||||
/// This function will have the same runtime errors as generated from `pop` and
|
|
||||||
/// `set` (the key must not currently be on loan
|
#[unsafe_destructor]
|
||||||
pub fn modify<T: 'static>(key: Key<T>, f: |Option<T>| -> Option<T>) {
|
impl<T: 'static> Drop for Ref<T> {
|
||||||
match f(pop(key)) {
|
fn drop(&mut self) {
|
||||||
Some(next) => { set(key, next); }
|
let map = unsafe { get_local_map() };
|
||||||
None => {}
|
|
||||||
|
let (_, _, ref mut loan) = *map.get_mut(self.index).get_mut_ref();
|
||||||
|
*loan -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,55 +276,36 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tls_multitask() {
|
fn test_tls_multitask() {
|
||||||
static my_key: Key<~str> = &Key;
|
static my_key: Key<~str> = &Key;
|
||||||
set(my_key, "parent data".to_owned());
|
my_key.replace(Some("parent data".to_owned()));
|
||||||
task::spawn(proc() {
|
task::spawn(proc() {
|
||||||
// TLS shouldn't carry over.
|
// TLS shouldn't carry over.
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).is_none());
|
assert!(my_key.get().is_none());
|
||||||
set(my_key, "child data".to_owned());
|
my_key.replace(Some("child data".to_owned()));
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() ==
|
assert!(my_key.get().get_ref().as_slice() == "child data");
|
||||||
"child data".to_owned());
|
|
||||||
// should be cleaned up for us
|
// should be cleaned up for us
|
||||||
});
|
});
|
||||||
|
|
||||||
// Must work multiple times
|
// Must work multiple times
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned());
|
assert!(my_key.get().unwrap().as_slice() == "parent data");
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned());
|
assert!(my_key.get().unwrap().as_slice() == "parent data");
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned());
|
assert!(my_key.get().unwrap().as_slice() == "parent data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tls_overwrite() {
|
fn test_tls_overwrite() {
|
||||||
static my_key: Key<~str> = &Key;
|
static my_key: Key<~str> = &Key;
|
||||||
set(my_key, "first data".to_owned());
|
my_key.replace(Some("first data".to_owned()));
|
||||||
set(my_key, "next data".to_owned()); // Shouldn't leak.
|
my_key.replace(Some("next data".to_owned())); // Shouldn't leak.
|
||||||
assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "next data".to_owned());
|
assert!(my_key.get().unwrap().as_slice() == "next data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tls_pop() {
|
fn test_tls_pop() {
|
||||||
static my_key: Key<~str> = &Key;
|
static my_key: Key<~str> = &Key;
|
||||||
set(my_key, "weasel".to_owned());
|
my_key.replace(Some("weasel".to_owned()));
|
||||||
assert!(pop(my_key).unwrap() == "weasel".to_owned());
|
assert!(my_key.replace(None).unwrap() == "weasel".to_owned());
|
||||||
// Pop must remove the data from the map.
|
// Pop must remove the data from the map.
|
||||||
assert!(pop(my_key).is_none());
|
assert!(my_key.replace(None).is_none());
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tls_modify() {
|
|
||||||
static my_key: Key<~str> = &Key;
|
|
||||||
modify(my_key, |data| {
|
|
||||||
match data {
|
|
||||||
Some(ref val) => fail!("unwelcome value: {}", *val),
|
|
||||||
None => Some("first data".to_owned())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modify(my_key, |data| {
|
|
||||||
match data.as_ref().map(|s| s.as_slice()) {
|
|
||||||
Some("first data") => Some("next data".to_owned()),
|
|
||||||
Some(ref val) => fail!("wrong value: {}", *val),
|
|
||||||
None => fail!("missing value")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert!(pop(my_key).unwrap() == "next data".to_owned());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -423,7 +318,7 @@ mod tests {
|
||||||
// a stack smaller than 1 MB.
|
// a stack smaller than 1 MB.
|
||||||
static my_key: Key<~str> = &Key;
|
static my_key: Key<~str> = &Key;
|
||||||
task::spawn(proc() {
|
task::spawn(proc() {
|
||||||
set(my_key, "hax".to_owned());
|
my_key.replace(Some("hax".to_owned()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,28 +328,27 @@ mod tests {
|
||||||
static box_key: Key<@()> = &Key;
|
static box_key: Key<@()> = &Key;
|
||||||
static int_key: Key<int> = &Key;
|
static int_key: Key<int> = &Key;
|
||||||
task::spawn(proc() {
|
task::spawn(proc() {
|
||||||
set(str_key, "string data".to_owned());
|
str_key.replace(Some("string data".to_owned()));
|
||||||
set(box_key, @());
|
box_key.replace(Some(@()));
|
||||||
set(int_key, 42);
|
int_key.replace(Some(42));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(dead_code)]
|
|
||||||
fn test_tls_overwrite_multiple_types() {
|
fn test_tls_overwrite_multiple_types() {
|
||||||
static str_key: Key<~str> = &Key;
|
static str_key: Key<~str> = &Key;
|
||||||
static box_key: Key<@()> = &Key;
|
static box_key: Key<@()> = &Key;
|
||||||
static int_key: Key<int> = &Key;
|
static int_key: Key<int> = &Key;
|
||||||
task::spawn(proc() {
|
task::spawn(proc() {
|
||||||
set(str_key, "string data".to_owned());
|
str_key.replace(Some("string data".to_owned()));
|
||||||
set(str_key, "string data 2".to_owned());
|
str_key.replace(Some("string data 2".to_owned()));
|
||||||
set(box_key, @());
|
box_key.replace(Some(@()));
|
||||||
set(box_key, @());
|
box_key.replace(Some(@()));
|
||||||
set(int_key, 42);
|
int_key.replace(Some(42));
|
||||||
// This could cause a segfault if overwriting-destruction is done
|
// This could cause a segfault if overwriting-destruction is done
|
||||||
// with the crazy polymorphic transmute rather than the provided
|
// with the crazy polymorphic transmute rather than the provided
|
||||||
// finaliser.
|
// finaliser.
|
||||||
set(int_key, 31337);
|
int_key.replace(Some(31337));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,17 +358,16 @@ mod tests {
|
||||||
static str_key: Key<~str> = &Key;
|
static str_key: Key<~str> = &Key;
|
||||||
static box_key: Key<@()> = &Key;
|
static box_key: Key<@()> = &Key;
|
||||||
static int_key: Key<int> = &Key;
|
static int_key: Key<int> = &Key;
|
||||||
set(str_key, "parent data".to_owned());
|
str_key.replace(Some("parent data".to_owned()));
|
||||||
set(box_key, @());
|
box_key.replace(Some(@()));
|
||||||
task::spawn(proc() {
|
task::spawn(proc() {
|
||||||
// spawn_linked
|
str_key.replace(Some("string data".to_owned()));
|
||||||
set(str_key, "string data".to_owned());
|
box_key.replace(Some(@()));
|
||||||
set(box_key, @());
|
int_key.replace(Some(42));
|
||||||
set(int_key, 42);
|
|
||||||
fail!();
|
fail!();
|
||||||
});
|
});
|
||||||
// Not quite nondeterministic.
|
// Not quite nondeterministic.
|
||||||
set(int_key, 31337);
|
int_key.replace(Some(31337));
|
||||||
fail!();
|
fail!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,42 +375,24 @@ mod tests {
|
||||||
fn test_static_pointer() {
|
fn test_static_pointer() {
|
||||||
static key: Key<&'static int> = &Key;
|
static key: Key<&'static int> = &Key;
|
||||||
static VALUE: int = 0;
|
static VALUE: int = 0;
|
||||||
let v: &'static int = &VALUE;
|
key.replace(Some(&VALUE));
|
||||||
set(key, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_owned() {
|
fn test_owned() {
|
||||||
static key: Key<Box<int>> = &Key;
|
static key: Key<Box<int>> = &Key;
|
||||||
set(key, box 1);
|
key.replace(Some(box 1));
|
||||||
|
|
||||||
get(key, |v| {
|
{
|
||||||
get(key, |v| {
|
let k1 = key.get().unwrap();
|
||||||
get(key, |v| {
|
let k2 = key.get().unwrap();
|
||||||
assert_eq!(**v.unwrap(), 1);
|
let k3 = key.get().unwrap();
|
||||||
});
|
assert_eq!(**k1, 1);
|
||||||
assert_eq!(**v.unwrap(), 1);
|
assert_eq!(**k2, 1);
|
||||||
});
|
assert_eq!(**k3, 1);
|
||||||
assert_eq!(**v.unwrap(), 1);
|
}
|
||||||
});
|
key.replace(Some(box 2));
|
||||||
set(key, box 2);
|
assert_eq!(**key.get().unwrap(), 2);
|
||||||
get(key, |v| {
|
|
||||||
assert_eq!(**v.unwrap(), 2);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_mut() {
|
|
||||||
static key: Key<int> = &Key;
|
|
||||||
set(key, 1);
|
|
||||||
|
|
||||||
get_mut(key, |v| {
|
|
||||||
*v.unwrap() = 2;
|
|
||||||
});
|
|
||||||
|
|
||||||
get(key, |v| {
|
|
||||||
assert_eq!(*v.unwrap(), 2);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -527,56 +402,26 @@ mod tests {
|
||||||
static key3: Key<int> = &Key;
|
static key3: Key<int> = &Key;
|
||||||
static key4: Key<int> = &Key;
|
static key4: Key<int> = &Key;
|
||||||
static key5: Key<int> = &Key;
|
static key5: Key<int> = &Key;
|
||||||
set(key1, 1);
|
key1.replace(Some(1));
|
||||||
set(key2, 2);
|
key2.replace(Some(2));
|
||||||
set(key3, 3);
|
key3.replace(Some(3));
|
||||||
set(key4, 4);
|
key4.replace(Some(4));
|
||||||
set(key5, 5);
|
key5.replace(Some(5));
|
||||||
|
|
||||||
get(key1, |x| assert_eq!(*x.unwrap(), 1));
|
assert_eq!(*key1.get().unwrap(), 1);
|
||||||
get(key2, |x| assert_eq!(*x.unwrap(), 2));
|
assert_eq!(*key2.get().unwrap(), 2);
|
||||||
get(key3, |x| assert_eq!(*x.unwrap(), 3));
|
assert_eq!(*key3.get().unwrap(), 3);
|
||||||
get(key4, |x| assert_eq!(*x.unwrap(), 4));
|
assert_eq!(*key4.get().unwrap(), 4);
|
||||||
get(key5, |x| assert_eq!(*x.unwrap(), 5));
|
assert_eq!(*key5.get().unwrap(), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
#[should_fail]
|
||||||
fn test_nested_get_set1() {
|
fn test_nested_get_set1() {
|
||||||
static key: Key<int> = &Key;
|
static key: Key<int> = &Key;
|
||||||
set(key, 4);
|
key.replace(Some(4));
|
||||||
get(key, |_| {
|
|
||||||
set(key, 4);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let _k = key.get();
|
||||||
#[should_fail]
|
key.replace(Some(4));
|
||||||
fn test_nested_get_mut2() {
|
|
||||||
static key: Key<int> = &Key;
|
|
||||||
set(key, 4);
|
|
||||||
get(key, |_| {
|
|
||||||
get_mut(key, |_| {})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_fail]
|
|
||||||
fn test_nested_get_mut3() {
|
|
||||||
static key: Key<int> = &Key;
|
|
||||||
set(key, 4);
|
|
||||||
get_mut(key, |_| {
|
|
||||||
get(key, |_| {})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_fail]
|
|
||||||
fn test_nested_get_mut4() {
|
|
||||||
static key: Key<int> = &Key;
|
|
||||||
set(key, 4);
|
|
||||||
get_mut(key, |_| {
|
|
||||||
get_mut(key, |_| {})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,12 +289,10 @@ macro_rules! println(
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::local_data;
|
|
||||||
///
|
|
||||||
/// local_data_key!(my_integer: int)
|
/// local_data_key!(my_integer: int)
|
||||||
///
|
///
|
||||||
/// local_data::set(my_integer, 2);
|
/// my_integer.replace(Some(2));
|
||||||
/// local_data::get(my_integer, |val| println!("{}", val.map(|i| *i)));
|
/// println!("{}", my_integer.get().map(|a| *a));
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! local_data_key(
|
macro_rules! local_data_key(
|
||||||
|
|
|
@ -414,13 +414,12 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tls() {
|
fn tls() {
|
||||||
use local_data;
|
|
||||||
local_data_key!(key: @~str)
|
local_data_key!(key: @~str)
|
||||||
local_data::set(key, @"data".to_owned());
|
key.replace(Some(@"data".to_owned()));
|
||||||
assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == "data".to_owned());
|
assert_eq!(key.get().unwrap().as_slice(), "data");
|
||||||
local_data_key!(key2: @~str)
|
local_data_key!(key2: @~str)
|
||||||
local_data::set(key2, @"data".to_owned());
|
key2.replace(Some(@"data".to_owned()));
|
||||||
assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == "data".to_owned());
|
assert_eq!(key2.get().unwrap().as_slice(), "data");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3112,7 +3112,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
#[should_fail]
|
||||||
fn test_from_elem_fail() {
|
fn test_from_elem_fail() {
|
||||||
use cast;
|
|
||||||
use cell::Cell;
|
use cell::Cell;
|
||||||
use rc::Rc;
|
use rc::Rc;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
use ast::{Ident, Mrk, Name, SyntaxContext};
|
use ast::{Ident, Mrk, Name, SyntaxContext};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::local_data;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -91,17 +90,14 @@ fn new_rename_internal(id: Ident,
|
||||||
pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
|
pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
|
||||||
local_data_key!(sctable_key: Rc<SCTable>)
|
local_data_key!(sctable_key: Rc<SCTable>)
|
||||||
|
|
||||||
local_data::get(sctable_key, |opt_ts| {
|
match sctable_key.get() {
|
||||||
let table = match opt_ts {
|
Some(ts) => op(&**ts),
|
||||||
None => {
|
None => {
|
||||||
let ts = Rc::new(new_sctable_internal());
|
let ts = Rc::new(new_sctable_internal());
|
||||||
local_data::set(sctable_key, ts.clone());
|
sctable_key.replace(Some(ts.clone()));
|
||||||
ts
|
op(&*ts)
|
||||||
}
|
}
|
||||||
Some(ts) => ts.clone()
|
}
|
||||||
};
|
|
||||||
op(&*table)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a fresh syntax context table with EmptyCtxt in slot zero
|
// Make a fresh syntax context table with EmptyCtxt in slot zero
|
||||||
|
@ -154,17 +150,14 @@ type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
|
||||||
fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
|
fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
|
||||||
local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
|
local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
|
||||||
|
|
||||||
local_data::get(resolve_table_key, |opt_ts| {
|
match resolve_table_key.get() {
|
||||||
let table = match opt_ts {
|
Some(ts) => op(&mut *ts.borrow_mut()),
|
||||||
None => {
|
None => {
|
||||||
let ts = Rc::new(RefCell::new(HashMap::new()));
|
let ts = Rc::new(RefCell::new(HashMap::new()));
|
||||||
local_data::set(resolve_table_key, ts.clone());
|
resolve_table_key.replace(Some(ts.clone()));
|
||||||
ts
|
op(&mut *ts.borrow_mut())
|
||||||
}
|
}
|
||||||
Some(ts) => ts.clone()
|
}
|
||||||
};
|
|
||||||
op(&mut *table.borrow_mut())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a syntax object to a name, per MTWT.
|
// Resolve a syntax object to a name, per MTWT.
|
||||||
|
|
|
@ -19,7 +19,6 @@ use util::interner;
|
||||||
use serialize::{Decodable, Decoder, Encodable, Encoder};
|
use serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::local_data;
|
|
||||||
use std::path::BytesContainer;
|
use std::path::BytesContainer;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::strbuf::StrBuf;
|
use std::strbuf::StrBuf;
|
||||||
|
@ -529,11 +528,11 @@ pub type IdentInterner = StrInterner;
|
||||||
// FIXME(eddyb) #8726 This should probably use a task-local reference.
|
// FIXME(eddyb) #8726 This should probably use a task-local reference.
|
||||||
pub fn get_ident_interner() -> Rc<IdentInterner> {
|
pub fn get_ident_interner() -> Rc<IdentInterner> {
|
||||||
local_data_key!(key: Rc<::parse::token::IdentInterner>)
|
local_data_key!(key: Rc<::parse::token::IdentInterner>)
|
||||||
match local_data::get(key, |k| k.map(|k| k.clone())) {
|
match key.get() {
|
||||||
Some(interner) => interner,
|
Some(interner) => interner.clone(),
|
||||||
None => {
|
None => {
|
||||||
let interner = Rc::new(mk_fresh_ident_interner());
|
let interner = Rc::new(mk_fresh_ident_interner());
|
||||||
local_data::set(key, interner.clone());
|
key.replace(Some(interner.clone()));
|
||||||
interner
|
interner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::local_data;
|
|
||||||
use syntax::ast::Name;
|
use syntax::ast::Name;
|
||||||
use syntax::ext::base::SyntaxExtension;
|
use syntax::ext::base::SyntaxExtension;
|
||||||
|
|
||||||
|
@ -30,6 +29,6 @@ impl Drop for Foo {
|
||||||
#[macro_registrar]
|
#[macro_registrar]
|
||||||
pub fn registrar(_: |Name, SyntaxExtension|) {
|
pub fn registrar(_: |Name, SyntaxExtension|) {
|
||||||
local_data_key!(foo: Box<Any:Send>);
|
local_data_key!(foo: Box<Any:Send>);
|
||||||
local_data::set(foo, box Foo { foo: 10 } as Box<Any:Send>);
|
foo.replace(Some(box Foo { foo: 10 } as Box<Any:Send>));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
// Testing that we can't store a reference it task-local storage
|
// Testing that we can't store a reference it task-local storage
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
|
|
||||||
local_data_key!(key: @&int)
|
local_data_key!(key: @&int)
|
||||||
//~^ ERROR missing lifetime specifier
|
//~^ ERROR missing lifetime specifier
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
|
|
||||||
// check that the local data keys are private by default.
|
// check that the local data keys are private by default.
|
||||||
|
|
||||||
mod bar {
|
mod bar {
|
||||||
|
@ -17,6 +15,6 @@ mod bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
local_data::set(bar::baz, -10.0);
|
bar::baz.replace(Some(-10.0));
|
||||||
//~^ ERROR static `baz` is private
|
//~^ ERROR static `baz` is private
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::local_data;
|
|
||||||
|
|
||||||
local_data_key!(foo: int)
|
local_data_key!(foo: int)
|
||||||
|
|
||||||
mod bar {
|
mod bar {
|
||||||
|
@ -17,12 +15,12 @@ mod bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
local_data::get(foo, |x| assert!(x.is_none()));
|
assert!(foo.get().is_none());
|
||||||
local_data::get(bar::baz, |y| assert!(y.is_none()));
|
assert!(bar::baz.get().is_none());
|
||||||
|
|
||||||
local_data::set(foo, 3);
|
foo.replace(Some(3));
|
||||||
local_data::set(bar::baz, -10.0);
|
bar::baz.replace(Some(-10.0));
|
||||||
|
|
||||||
local_data::get(foo, |x| assert_eq!(*x.unwrap(), 3));
|
assert_eq!(*foo.get().unwrap(), 3);
|
||||||
local_data::get(bar::baz, |y| assert_eq!(*y.unwrap(), -10.0));
|
assert_eq!(*bar::baz.get().unwrap(), -10.0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue