std: Add a new wasm32-unknown-unknown target

This commit adds a new target to the compiler: wasm32-unknown-unknown. This
target is a reimagining of what it looks like to generate WebAssembly code from
Rust. Instead of using Emscripten which can bring with it a weighty runtime this
instead is a target which uses only the LLVM backend for WebAssembly and a
"custom linker" for now which will hopefully one day be direct calls to lld.

Notable features of this target include:

* There is zero runtime footprint. The target assumes nothing exists other than
  the wasm32 instruction set.
* There is zero toolchain footprint beyond adding the target. No custom linker
  is needed, rustc contains everything.
* Very small wasm modules can be generated directly from Rust code using this
  target.
* Most of the standard library is stubbed out to return an error, but anything
  related to allocation works (aka `HashMap`, `Vec`, etc).
* Naturally, any `#[no_std]` crate should be 100% compatible with this new
  target.

This target is currently somewhat janky due to how linking works. The "linking"
is currently unconditional whole program LTO (aka LLVM is being used as a
linker). Naturally that means compiling programs is pretty slow! Eventually
though this target should have a linker.

This target is also intended to be quite experimental. I'm hoping that this can
act as a catalyst for further experimentation in Rust with WebAssembly. Breaking
changes are very likely to land to this target, so it's not recommended to rely
on it in any critical capacity yet. We'll let you know when it's "production
ready".

---

Currently testing-wise this target is looking pretty good but isn't complete.
I've got almost the entire `run-pass` test suite working with this target (lots
of tests ignored, but many passing as well). The `core` test suite is still
getting LLVM bugs fixed to get that working and will take some time. Relatively
simple programs all seem to work though!

---

It's worth nothing that you may not immediately see the "smallest possible wasm
module" for the input you feed to rustc. For various reasons it's very difficult
to get rid of the final "bloat" in vanilla rustc (again, a real linker should
fix all this). For now what you'll have to do is:

    cargo install --git https://github.com/alexcrichton/wasm-gc
    wasm-gc foo.wasm bar.wasm

And then `bar.wasm` should be the smallest we can get it!

---

In any case for now I'd love feedback on this, particularly on the various
integration points if you've got better ideas of how to approach them!
This commit is contained in:
Alex Crichton 2017-10-22 20:01:00 -07:00
parent 481b42b507
commit 80ff0f74b0
162 changed files with 3644 additions and 440 deletions

6
.gitmodules vendored
View File

@ -42,3 +42,9 @@
[submodule "src/tools/miri"]
path = src/tools/miri
url = https://github.com/solson/miri.git
[submodule "src/dlmalloc"]
path = src/dlmalloc
url = https://github.com/alexcrichton/dlmalloc-rs.git
[submodule "src/binaryen"]
path = src/binaryen
url = https://github.com/alexcrichton/binaryen.git

22
src/Cargo.lock generated
View File

@ -50,6 +50,7 @@ version = "0.0.0"
dependencies = [
"alloc 0.0.0",
"core 0.0.0",
"dlmalloc 0.0.0",
"libc 0.0.0",
]
@ -372,9 +373,6 @@ version = "0.1.0"
[[package]]
name = "core"
version = "0.0.0"
dependencies = [
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-foundation"
@ -509,6 +507,14 @@ name = "diff"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dlmalloc"
version = "0.0.0"
dependencies = [
"alloc 0.0.0",
"core 0.0.0",
]
[[package]]
name = "docopt"
version = "0.8.1"
@ -1608,6 +1614,15 @@ dependencies = [
"syntax 0.0.0",
]
[[package]]
name = "rustc_binaryen"
version = "0.0.0"
dependencies = [
"cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc_borrowck"
version = "0.0.0"
@ -1880,6 +1895,7 @@ dependencies = [
"rustc_allocator 0.0.0",
"rustc_apfloat 0.0.0",
"rustc_back 0.0.0",
"rustc_binaryen 0.0.0",
"rustc_const_math 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",

1
src/binaryen Submodule

@ -0,0 +1 @@
Subproject commit 1c9bf65aa0e371b84755a8ddd6e79497fac57171

View File

@ -1211,7 +1211,8 @@ impl Step for Crate {
// ends up messing with various mtime calculations and such.
if !name.contains("jemalloc") &&
*name != *"build_helper" &&
!(name.starts_with("rustc_") && name.ends_with("san")) {
!(name.starts_with("rustc_") && name.ends_with("san")) &&
name != "dlmalloc" {
cargo.arg("-p").arg(&format!("{}:0.0.0", name));
}
for dep in build.crates[&name].deps.iter() {

View File

@ -672,6 +672,9 @@ fn copy_src_dirs(build: &Build, src_dirs: &[&str], exclude_dirs: &[&str], dst_di
spath.ends_with(".s")) {
return false
}
if spath.contains("test/emscripten") || spath.contains("test\\emscripten") {
return false
}
let full_path = Path::new(dir).join(path);
if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) {
@ -736,6 +739,7 @@ impl Step for Src {
// (essentially libstd and all of its path dependencies)
let std_src_dirs = [
"src/build_helper",
"src/dlmalloc",
"src/liballoc",
"src/liballoc_jemalloc",
"src/liballoc_system",
@ -754,6 +758,7 @@ impl Step for Src {
"src/libunwind",
"src/rustc/compiler_builtins_shim",
"src/rustc/libc_shim",
"src/rustc/dlmalloc_shim",
"src/libtest",
"src/libterm",
"src/jemalloc",

1
src/dlmalloc Submodule

@ -0,0 +1 @@
Subproject commit d3812c3accaee7ad23068ed4fc089cc05c7a538f

119
src/etc/wasm32-shim.js Normal file
View File

@ -0,0 +1,119 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This is a small "shim" program which is used when wasm32 unit tests are run
// in this repository. This program is intended to be run in node.js and will
// load a wasm module into memory, instantiate it with a set of imports, and
// then run it.
//
// There's a bunch of helper functions defined here in `imports.env`, but note
// that most of them aren't actually needed to execute most programs. Many of
// these are just intended for completeness or debugging. Hopefully over time
// nothing here is needed for completeness.
const fs = require('fs');
const process = require('process');
const buffer = fs.readFileSync(process.argv[2]);
Error.stackTraceLimit = 20;
let m = new WebAssembly.Module(buffer);
let memory = null;
function copystr(a, b) {
if (memory === null) {
return null
}
let view = new Uint8Array(memory.buffer).slice(a, a + b);
return String.fromCharCode.apply(null, view);
}
let imports = {};
imports.env = {
// These are generated by LLVM itself for various intrinsic calls. Hopefully
// one day this is not necessary and something will automatically do this.
fmod: function(x, y) { return x % y; },
exp2: function(x) { return Math.pow(2, x); },
exp2f: function(x) { return Math.pow(2, x); },
ldexp: function(x, y) { return x * Math.pow(2, y); },
ldexpf: function(x, y) { return x * Math.pow(2, y); },
log10: function(x) { return Math.log10(x); },
log10f: function(x) { return Math.log10(x); },
// These are called in src/libstd/sys/wasm/stdio.rs and are used when
// debugging is enabled.
rust_wasm_write_stdout: function(a, b) {
let s = copystr(a, b);
if (s !== null) {
process.stdout.write(s);
}
},
rust_wasm_write_stderr: function(a, b) {
let s = copystr(a, b);
if (s !== null) {
process.stderr.write(s);
}
},
// These are called in src/libstd/sys/wasm/args.rs and are used when
// debugging is enabled.
rust_wasm_args_count: function() {
if (memory === null)
return 0;
return process.argv.length - 2;
},
rust_wasm_args_arg_size: function(i) {
return process.argv[i + 2].length;
},
rust_wasm_args_arg_fill: function(idx, ptr) {
let arg = process.argv[idx + 2];
let view = new Uint8Array(memory.buffer);
for (var i = 0; i < arg.length; i++) {
view[ptr + i] = arg.charCodeAt(i);
}
},
// These are called in src/libstd/sys/wasm/os.rs and are used when
// debugging is enabled.
rust_wasm_getenv_len: function(a, b) {
let key = copystr(a, b);
if (key === null) {
return -1;
}
if (!(key in process.env)) {
return -1;
}
return process.env[key].length;
},
rust_wasm_getenv_data: function(a, b, ptr) {
let key = copystr(a, b);
let value = process.env[key];
let view = new Uint8Array(memory.buffer);
for (var i = 0; i < value.length; i++) {
view[ptr + i] = value.charCodeAt(i);
}
},
};
let module_imports = WebAssembly.Module.imports(m);
for (var i = 0; i < module_imports.length; i++) {
let imp = module_imports[i];
if (imp.module != 'env') {
continue
}
if (imp.name == 'memory' && imp.kind == 'memory') {
memory = new WebAssembly.Memory({initial: 20});
imports.env.memory = memory;
}
}
let instance = new WebAssembly.Instance(m, imports);

View File

@ -31,7 +31,7 @@ fn main() {
let host = env::var("HOST").expect("HOST was not set");
if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") ||
target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") ||
target.contains("redox") {
target.contains("redox") || target.contains("wasm32") {
println!("cargo:rustc-cfg=dummy_jemalloc");
return;
}

View File

@ -13,3 +13,7 @@ doc = false
alloc = { path = "../liballoc" }
core = { path = "../libcore" }
libc = { path = "../rustc/libc_shim" }
# See comments in the source for what this dependency is
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
dlmalloc = { path = "../rustc/dlmalloc_shim" }

View File

@ -34,12 +34,14 @@
target_arch = "powerpc64",
target_arch = "asmjs",
target_arch = "wasm32")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 16;
extern crate alloc;
@ -458,3 +460,91 @@ mod platform {
}
}
}
// This is an implementation of a global allocator on the wasm32 platform when
// emscripten is not in use. In that situation there's no actual runtime for us
// to lean on for allocation, so instead we provide our own!
//
// The wasm32 instruction set has two instructions for getting the current
// amount of memory and growing the amount of memory. These instructions are the
// foundation on which we're able to build an allocator, so we do so! Note that
// the instructions are also pretty "global" and this is the "global" allocator
// after all!
//
// The current allocator here is the `dlmalloc` crate which we've got included
// in the rust-lang/rust repository as a submodule. The crate is a port of
// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
// for now which is currently technically required (can't link with C yet).
//
// The crate itself provides a global allocator which on wasm has no
// synchronization as there are no threads!
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod platform {
extern crate dlmalloc;
use alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace};
use System;
use self::dlmalloc::GlobalDlmalloc;
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl<'a> Alloc for &'a System {
#[inline]
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
GlobalDlmalloc.alloc(layout)
}
#[inline]
unsafe fn alloc_zeroed(&mut self, layout: Layout)
-> Result<*mut u8, AllocErr>
{
GlobalDlmalloc.alloc_zeroed(layout)
}
#[inline]
unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
GlobalDlmalloc.dealloc(ptr, layout)
}
#[inline]
unsafe fn realloc(&mut self,
ptr: *mut u8,
old_layout: Layout,
new_layout: Layout) -> Result<*mut u8, AllocErr> {
GlobalDlmalloc.realloc(ptr, old_layout, new_layout)
}
#[inline]
fn usable_size(&self, layout: &Layout) -> (usize, usize) {
GlobalDlmalloc.usable_size(layout)
}
#[inline]
unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
GlobalDlmalloc.alloc_excess(layout)
}
#[inline]
unsafe fn realloc_excess(&mut self,
ptr: *mut u8,
layout: Layout,
new_layout: Layout) -> Result<Excess, AllocErr> {
GlobalDlmalloc.realloc_excess(ptr, layout, new_layout)
}
#[inline]
unsafe fn grow_in_place(&mut self,
ptr: *mut u8,
layout: Layout,
new_layout: Layout) -> Result<(), CannotReallocInPlace> {
GlobalDlmalloc.grow_in_place(ptr, layout, new_layout)
}
#[inline]
unsafe fn shrink_in_place(&mut self,
ptr: *mut u8,
layout: Layout,
new_layout: Layout) -> Result<(), CannotReallocInPlace> {
GlobalDlmalloc.shrink_in_place(ptr, layout, new_layout)
}
}
}

View File

@ -9,9 +9,6 @@ path = "lib.rs"
test = false
bench = false
[dev-dependencies]
rand = "0.3"
[[test]]
name = "coretests"
path = "../libcore/tests/lib.rs"

View File

@ -27,7 +27,6 @@
#![feature(iter_rfind)]
#![feature(iter_rfold)]
#![feature(nonzero)]
#![feature(rand)]
#![feature(raw)]
#![feature(refcell_replace_swap)]
#![feature(sip_hash_13)]
@ -48,7 +47,6 @@
extern crate core;
extern crate test;
extern crate rand;
mod any;
mod array;

View File

@ -9,9 +9,7 @@
// except according to those terms.
use std::prelude::v1::*;
use std::{str, mem, i16, f32, f64, fmt};
use rand::{self, Rand, XorShiftRng};
use rand::distributions::{IndependentSample, Range};
use std::{str, i16, f32, f64, fmt};
use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};
use core::num::flt2dec::{MAX_SIG_DIGITS, round_up, Part, Formatted, Sign};
@ -463,87 +461,6 @@ pub fn more_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8]
exp: 0, inclusive: false} => b"99999999999999999", 17);
}
fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
V: FnMut(usize) -> Decoded {
assert!(k <= 1024);
let mut npassed = 0; // f(x) = Some(g(x))
let mut nignored = 0; // f(x) = None
for i in 0..n {
if (i & 0xfffff) == 0 {
println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})",
i, n, nignored, npassed, i - nignored - npassed);
}
let decoded = v(i);
let mut buf1 = [0; 1024];
if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
let mut buf2 = [0; 1024];
let (len2, e2) = g(&decoded, &mut buf2[..k]);
if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
npassed += 1;
} else {
println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
str::from_utf8(&buf2[..len2]).unwrap(), e2);
}
} else {
nignored += 1;
}
}
println!("{}({}): done, ignored={} passed={} failed={}",
func, k, nignored, npassed, n - nignored - npassed);
assert!(nignored + npassed == n,
"{}({}): {} out of {} values returns an incorrect value!",
func, k, n - nignored - npassed, n);
(npassed, nignored)
}
pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
iterate("f32_random_equivalence_test", k, n, f, g, |_| {
let i: u32 = f32_range.ind_sample(&mut rng);
let x: f32 = unsafe {mem::transmute(i)};
decode_finite(x)
});
}
pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
iterate("f64_random_equivalence_test", k, n, f, g, |_| {
let i: u64 = f64_range.ind_sample(&mut rng);
let x: f64 = unsafe {mem::transmute(i)};
decode_finite(x)
});
}
pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
// we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
// so why not simply testing all of them?
//
// this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
// but with `-C opt-level=3 -C lto` this only takes about an hour or so.
// iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges
let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test",
k, 0x7f7f_ffff, f, g, |i: usize| {
let x: f32 = unsafe {mem::transmute(i as u32 + 1)};
decode_finite(x)
});
assert_eq!((npassed, nignored), (2121451881, 17643158));
}
fn to_string_with_parts<F>(mut f: F) -> String
where F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a> {
let mut buf = [0; 1024];

View File

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::i16;
use super::super::*;
use core::num::flt2dec::strategy::grisu::*;
@ -46,35 +45,6 @@ fn shortest_sanity_test() {
more_shortest_sanity_test(format_shortest);
}
#[test]
fn shortest_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
}
#[test] #[ignore] // it is too expensive
fn shortest_f32_exhaustive_equivalence_test() {
// it is hard to directly test the optimality of the output, but we can at least test if
// two different algorithms agree to each other.
//
// this reports the progress and the number of f32 values returned `None`.
// with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
// `done, ignored=17643158 passed=2121451881 failed=0`.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}
#[test] #[ignore] // it is too expensive
fn shortest_f64_hard_random_equivalence_test() {
// this again probably has to use appropriate rustc flags.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback,
MAX_SIG_DIGITS, 100_000_000);
}
#[test]
fn exact_sanity_test() {
// See comments in dragon.rs's exact_sanity_test for why this test is
@ -85,24 +55,6 @@ fn exact_sanity_test() {
f32_exact_sanity_test(format_exact);
}
#[test]
fn exact_f32_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}
#[test]
fn exact_f64_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}
#[test]
fn test_to_shortest_str() {
to_shortest_str_test(format_shortest);

View File

@ -8,10 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::cmp::Ordering::{Equal, Greater, Less};
use core::slice::heapsort;
use core::result::Result::{Ok, Err};
use rand::{Rng, XorShiftRng};
#[test]
fn test_binary_search() {
@ -290,68 +287,3 @@ fn test_rotate() {
assert_eq!(a[(i+k)%N], i);
}
}
#[test]
fn sort_unstable() {
let mut v = [0; 600];
let mut tmp = [0; 600];
let mut rng = XorShiftRng::new_unseeded();
for len in (2..25).chain(500..510) {
let v = &mut v[0..len];
let tmp = &mut tmp[0..len];
for &modulus in &[5, 10, 100, 1000] {
for _ in 0..100 {
for i in 0..len {
v[i] = rng.gen::<i32>() % modulus;
}
// Sort in default order.
tmp.copy_from_slice(v);
tmp.sort_unstable();
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Sort in ascending order.
tmp.copy_from_slice(v);
tmp.sort_unstable_by(|a, b| a.cmp(b));
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Sort in descending order.
tmp.copy_from_slice(v);
tmp.sort_unstable_by(|a, b| b.cmp(a));
assert!(tmp.windows(2).all(|w| w[0] >= w[1]));
// Test heapsort using `<` operator.
tmp.copy_from_slice(v);
heapsort(tmp, |a, b| a < b);
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Test heapsort using `>` operator.
tmp.copy_from_slice(v);
heapsort(tmp, |a, b| a > b);
assert!(tmp.windows(2).all(|w| w[0] >= w[1]));
}
}
}
// Sort using a completely random comparison function.
// This will reorder the elements *somehow*, but won't panic.
for i in 0..v.len() {
v[i] = i as i32;
}
v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap());
v.sort_unstable();
for i in 0..v.len() {
assert_eq!(v[i], i as i32);
}
// Should not panic.
[0i32; 0].sort_unstable();
[(); 10].sort_unstable();
[(); 100].sort_unstable();
let mut v = [0xDEADBEEFu64];
v.sort_unstable();
assert!(v == [0xDEADBEEF]);
}

View File

@ -20,13 +20,13 @@
html_root_url = "https://doc.rust-lang.org/nightly/",
issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
#![deny(warnings)]
#![feature(staged_api)]
#![panic_runtime]
#![allow(unused_features)]
#![feature(core_intrinsics)]
#![feature(libc)]
#![feature(panic_runtime)]
#![cfg_attr(unix, feature(libc))]
#![cfg_attr(any(target_os = "redox", windows), feature(core_intrinsics))]
#![feature(staged_api)]
// Rust's "try" function, but if we're aborting on panics we just call the
// function as there's nothing else we need to do here.
@ -59,7 +59,9 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 {
libc::abort();
}
#[cfg(any(target_os = "redox", windows))]
#[cfg(any(target_os = "redox",
windows,
all(target_arch = "wasm32", not(target_os = "emscripten"))))]
unsafe fn abort() -> ! {
core::intrinsics::abort();
}
@ -92,7 +94,6 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 {
// binaries, but it should never be called as we don't link in an unwinding
// runtime at all.
pub mod personalities {
#[no_mangle]
#[cfg(not(all(target_os = "windows",
target_env = "gnu",

View File

@ -34,9 +34,7 @@
#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(libc)]
#![cfg_attr(not(any(target_env = "msvc",
all(windows, target_arch = "x86_64", target_env = "gnu"))),
feature(panic_unwind))]
#![feature(panic_unwind)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(unwind_attributes)]
@ -80,6 +78,10 @@ mod imp;
#[path = "emcc.rs"]
mod imp;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[path = "wasm32.rs"]
mod imp;
mod dwarf;
mod windows;

View File

@ -0,0 +1,29 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Unwinding for wasm32
//!
//! Right now we don't support this, so this is just stubs
use alloc::boxed::Box;
use core::any::Any;
use core::intrinsics;
pub fn payload() -> *mut u8 {
0 as *mut u8
}
pub unsafe fn cleanup(_ptr: *mut u8) -> Box<Any + Send> {
intrinsics::abort()
}
pub unsafe fn panic(_data: Box<Any + Send>) -> u32 {
intrinsics::abort()
}

View File

@ -432,7 +432,7 @@ impl Session {
self.opts.debugging_opts.borrowck_mir
}
pub fn lto(&self) -> bool {
self.opts.cg.lto
self.opts.cg.lto || self.target.target.options.requires_lto
}
/// Returns the panic strategy for this compile session. If the user explicitly selected one
/// using '-C panic', use that, otherwise use the panic strategy defined by the target.

View File

@ -84,6 +84,7 @@ macro_rules! linker_flavor {
linker_flavor! {
(Em, "em"),
(Binaryen, "binaryen"),
(Gcc, "gcc"),
(Ld, "ld"),
(Msvc, "msvc"),

View File

@ -218,6 +218,7 @@ supported_targets! {
("asmjs-unknown-emscripten", asmjs_unknown_emscripten),
("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
("wasm32-unknown-unknown", wasm32_unknown_unknown),
("wasm32-experimental-emscripten", wasm32_experimental_emscripten),
("thumbv6m-none-eabi", thumbv6m_none_eabi),
@ -303,6 +304,8 @@ pub struct TargetOptions {
pub features: String,
/// Whether dynamic linking is available on this target. Defaults to false.
pub dynamic_linking: bool,
/// If dynamic linking is available, whether only cdylibs are supported.
pub only_cdylib: bool,
/// Whether executables are available on this target. iOS, for example, only allows static
/// libraries. Defaults to false.
pub executables: bool,
@ -439,6 +442,17 @@ pub struct TargetOptions {
/// Whether to generate trap instructions in places where optimization would
/// otherwise produce control flow that falls through into unrelated memory.
pub trap_unreachable: bool,
/// This target requires everything to be compiled with LTO to emit a final
/// executable, aka there is no native linker for this target.
pub requires_lto: bool,
/// This target has no support for threads.
pub singlethread: bool,
/// Whether library functions call lowering/optimization is disabled in LLVM
/// for this target unconditionally.
pub no_builtins: bool,
}
impl Default for TargetOptions {
@ -454,6 +468,7 @@ impl Default for TargetOptions {
cpu: "generic".to_string(),
features: "".to_string(),
dynamic_linking: false,
only_cdylib: false,
executables: false,
relocation_model: "pic".to_string(),
code_model: "default".to_string(),
@ -503,6 +518,9 @@ impl Default for TargetOptions {
min_global_align: None,
default_codegen_units: None,
trap_unreachable: true,
requires_lto: false,
singlethread: false,
no_builtins: false,
}
}
}
@ -702,6 +720,7 @@ impl Target {
key!(cpu);
key!(features);
key!(dynamic_linking, bool);
key!(only_cdylib, bool);
key!(executables, bool);
key!(relocation_model);
key!(code_model);
@ -745,6 +764,9 @@ impl Target {
key!(min_global_align, Option<u64>);
key!(default_codegen_units, Option<u64>);
key!(trap_unreachable, bool);
key!(requires_lto, bool);
key!(singlethread, bool);
key!(no_builtins, bool);
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
for name in array.iter().filter_map(|abi| abi.as_string()) {
@ -896,6 +918,7 @@ impl ToJson for Target {
target_option_val!(cpu);
target_option_val!(features);
target_option_val!(dynamic_linking);
target_option_val!(only_cdylib);
target_option_val!(executables);
target_option_val!(relocation_model);
target_option_val!(code_model);
@ -939,6 +962,9 @@ impl ToJson for Target {
target_option_val!(min_global_align);
target_option_val!(default_codegen_units);
target_option_val!(trap_unreachable);
target_option_val!(requires_lto);
target_option_val!(singlethread);
target_option_val!(no_builtins);
if default.abi_blacklist != self.options.abi_blacklist {
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()

View File

@ -0,0 +1,104 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// The wasm32-unknown-unknown target is currently a highly experimental version
// of a wasm-based target which does *not* use the Emscripten toolchain. Instead
// this is a pretty flavorful (aka hacked up) target right now. The definition
// and semantics of this target are likely to change and so this shouldn't be
// relied on just yet.
//
// In general everyone is currently waiting on a linker for wasm code. In the
// meantime we have no means of actually making use of the traditional separate
// compilation model. At a high level this means that assembling Rust programs
// into a WebAssembly program looks like:
//
// 1. All intermediate artifacts are LLVM bytecode. We'll be using LLVM as
// a linker later on.
// 2. For the final artifact we emit one giant assembly file (WebAssembly
// doesn't have an object file format). To do this we force LTO to be turned
// on (`requires_lto` below) to ensure all Rust code is in one module. Any
// "linked" C library is basically just ignored.
// 3. Using LLVM we emit a `foo.s` file (assembly) with some... what I can only
// describe as arcane syntax. From there we need to actually change this
// into a wasm module. For this step we use the `binaryen` project. This
// project is mostly intended as a WebAssembly code generator, but for now
// we're just using its LLVM-assembly-to-wasm-module conversion utilities.
//
// And voila, out comes a web assembly module! There's some various tweaks here
// and there, but that's the high level at least. Note that this will be
// rethought from the ground up once a linker (lld) is available, so this is all
// temporary and should improve in the future.
use LinkerFlavor;
use super::{Target, TargetOptions, PanicStrategy};
pub fn target() -> Result<Target, String> {
let opts = TargetOptions {
linker: "not-used".to_string(),
// we allow dynamic linking, but only cdylibs. Basically we allow a
// final library artifact that exports some symbols (a wasm module) but
// we don't allow intermediate `dylib` crate types
dynamic_linking: true,
only_cdylib: true,
// This means we'll just embed a `start` function in the wasm module
executables: true,
// relatively self-explanatory!
exe_suffix: ".wasm".to_string(),
dll_prefix: "".to_string(),
dll_suffix: ".wasm".to_string(),
linker_is_gnu: false,
// We're storing bitcode for now in all the rlibs
obj_is_bitcode: true,
// A bit of a lie, but "eh"
max_atomic_width: Some(32),
// Unwinding doesn't work right now, so the whole target unconditionally
// defaults to panic=abort. Note that this is guaranteed to change in
// the future once unwinding is implemented. Don't rely on this.
panic_strategy: PanicStrategy::Abort,
// There's no linker yet so we're forced to use LLVM as a linker. This
// means that we must always enable LTO for final artifacts.
requires_lto: true,
// Wasm doesn't have atomics yet, so tell LLVM that we're in a single
// threaded model which will legalize atomics to normal operations.
singlethread: true,
// Because we're always enabling LTO we can't enable builtin lowering as
// otherwise we'll lower the definition of the `memcpy` function to
// memcpy itself. Note that this is specifically because we're
// performing LTO with compiler-builtins.
no_builtins: true,
.. Default::default()
};
Ok(Target {
llvm_target: "wasm32-unknown-unknown".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
// This is basically guaranteed to change in the future, don't rely on
// this. Use `not(target_os = "emscripten")` for now.
target_os: "unknown".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(),
arch: "wasm32".to_string(),
// A bit of a lie, but it gets the job done
linker_flavor: LinkerFlavor::Binaryen,
options: opts,
})
}

View File

@ -0,0 +1,132 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This is a small C API inserted on top of the Binaryen C++ API which we use
// from Rust. Once we have a real linker for we'll be able to remove all this,
// and otherwise this is just all on a "as we need it" basis for now.
#include <stdint.h>
#include <string>
#include <stdlib.h>
#include "s2wasm.h"
#include "wasm-binary.h"
#include "wasm-linker.h"
using namespace wasm;
struct BinaryenRustModule {
BufferWithRandomAccess buffer;
};
struct BinaryenRustModuleOptions {
uint64_t globalBase;
bool debug;
uint64_t stackAllocation;
uint64_t initialMem;
uint64_t maxMem;
bool importMemory;
bool ignoreUnknownSymbols;
bool debugInfo;
std::string startFunction;
BinaryenRustModuleOptions() :
globalBase(0),
debug(false),
stackAllocation(0),
initialMem(0),
maxMem(0),
importMemory(false),
ignoreUnknownSymbols(false),
debugInfo(false),
startFunction("")
{}
};
extern "C" BinaryenRustModuleOptions*
BinaryenRustModuleOptionsCreate() {
return new BinaryenRustModuleOptions;
}
extern "C" void
BinaryenRustModuleOptionsFree(BinaryenRustModuleOptions *options) {
delete options;
}
extern "C" void
BinaryenRustModuleOptionsSetDebugInfo(BinaryenRustModuleOptions *options,
bool debugInfo) {
options->debugInfo = debugInfo;
}
extern "C" void
BinaryenRustModuleOptionsSetStart(BinaryenRustModuleOptions *options,
char *start) {
options->startFunction = start;
}
extern "C" void
BinaryenRustModuleOptionsSetStackAllocation(BinaryenRustModuleOptions *options,
uint64_t stack) {
options->stackAllocation = stack;
}
extern "C" void
BinaryenRustModuleOptionsSetImportMemory(BinaryenRustModuleOptions *options,
bool import) {
options->importMemory = import;
}
extern "C" BinaryenRustModule*
BinaryenRustModuleCreate(const BinaryenRustModuleOptions *options,
const char *assembly) {
Linker linker(
options->globalBase,
options->stackAllocation,
options->initialMem,
options->maxMem,
options->importMemory,
options->ignoreUnknownSymbols,
options->startFunction,
options->debug);
S2WasmBuilder mainbuilder(assembly, options->debug);
linker.linkObject(mainbuilder);
linker.layout();
auto ret = make_unique<BinaryenRustModule>();
{
WasmBinaryWriter writer(&linker.getOutput().wasm, ret->buffer, options->debug);
writer.setNamesSection(options->debugInfo);
// FIXME: support source maps?
// writer.setSourceMap(sourceMapStream.get(), sourceMapUrl);
// FIXME: support symbol maps?
// writer.setSymbolMap(symbolMap);
writer.write();
}
return ret.release();
}
extern "C" const uint8_t*
BinaryenRustModulePtr(const BinaryenRustModule *M) {
return M->buffer.data();
}
extern "C" size_t
BinaryenRustModuleLen(const BinaryenRustModule *M) {
return M->buffer.size();
}
extern "C" void
BinaryenRustModuleFree(BinaryenRustModule *M) {
delete M;
}

View File

@ -0,0 +1,16 @@
# Wondering what this crate is? Take a look at the `lib.rs`!
[package]
name = "rustc_binaryen"
version = "0.0.0"
authors = ["The Rust Project Developers"]
[lib]
path = "lib.rs"
[dependencies]
libc = "0.2"
[build-dependencies]
cmake = "0.1"
cc = "1.0"

View File

@ -0,0 +1,60 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate cc;
extern crate cmake;
use std::env;
use cmake::Config;
fn main() {
let target = env::var("TARGET").unwrap();
// Bring in `__emutls_get_address` which is apparently needed for now
if target.contains("pc-windows-gnu") {
println!("cargo:rustc-link-lib=gcc_eh");
println!("cargo:rustc-link-lib=pthread");
}
Config::new("../binaryen")
.define("BUILD_STATIC_LIB", "ON")
.build_target("binaryen")
.build();
// I couldn't figure out how to link just one of these, so link everything.
println!("cargo:rustc-link-lib=static=asmjs");
println!("cargo:rustc-link-lib=static=binaryen");
println!("cargo:rustc-link-lib=static=cfg");
println!("cargo:rustc-link-lib=static=emscripten-optimizer");
println!("cargo:rustc-link-lib=static=ir");
println!("cargo:rustc-link-lib=static=passes");
println!("cargo:rustc-link-lib=static=support");
println!("cargo:rustc-link-lib=static=wasm");
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo:rustc-link-search=native={}/build/lib", out_dir);
// Add in our own little shim along with some extra files that weren't
// included in the main build.
let mut cfg = cc::Build::new();
cfg.file("BinaryenWrapper.cpp")
.file("../binaryen/src/wasm-linker.cpp")
.file("../binaryen/src/wasm-emscripten.cpp")
.include("../binaryen/src")
.cpp_link_stdlib(None)
.warnings(false)
.cpp(true);
if !target.contains("msvc") {
cfg.flag("-std=c++11");
}
cfg.compile("binaryen_wrapper");
}

View File

@ -0,0 +1,150 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Rustc bindings to the binaryen project.
//!
//! This crate is a small shim around the binaryen project which provides us the
//! ability to take LLVM's output and generate a wasm module. Specifically this
//! only supports one operation, creating a module from LLVM's assembly format
//! and then serializing that module to a wasm module.
extern crate libc;
use std::slice;
use std::ffi::{CString, CStr};
/// In-memory representation of a serialized wasm module.
pub struct Module {
ptr: *mut BinaryenRustModule,
}
impl Module {
/// Creates a new wasm module from the LLVM-assembly provided (in a C string
/// format).
///
/// The actual module creation can be tweaked through the various options in
/// `ModuleOptions` as well. Any errors are just returned as a bland string.
pub fn new(assembly: &CStr, opts: &ModuleOptions) -> Result<Module, String> {
unsafe {
let ptr = BinaryenRustModuleCreate(opts.ptr, assembly.as_ptr());
if ptr.is_null() {
Err(format!("failed to create binaryen module"))
} else {
Ok(Module { ptr })
}
}
}
/// Returns the data of the serialized wasm module. This is a `foo.wasm`
/// file contents.
pub fn data(&self) -> &[u8] {
unsafe {
let ptr = BinaryenRustModulePtr(self.ptr);
let len = BinaryenRustModuleLen(self.ptr);
slice::from_raw_parts(ptr, len)
}
}
}
impl Drop for Module {
fn drop(&mut self) {
unsafe {
BinaryenRustModuleFree(self.ptr);
}
}
}
pub struct ModuleOptions {
ptr: *mut BinaryenRustModuleOptions,
}
impl ModuleOptions {
pub fn new() -> ModuleOptions {
unsafe {
let ptr = BinaryenRustModuleOptionsCreate();
ModuleOptions { ptr }
}
}
/// Turns on or off debug info.
///
/// From what I can tell this just creates a "names" section of the wasm
/// module which contains a table of the original function names.
pub fn debuginfo(&mut self, debug: bool) -> &mut Self {
unsafe {
BinaryenRustModuleOptionsSetDebugInfo(self.ptr, debug);
}
self
}
/// Configures a `start` function for the module, to be executed when it's
/// loaded.
pub fn start(&mut self, func: &str) -> &mut Self {
let func = CString::new(func).unwrap();
unsafe {
BinaryenRustModuleOptionsSetStart(self.ptr, func.as_ptr());
}
self
}
/// Configures how much stack is initially allocated for the module. 1MB is
/// probably good enough for now.
pub fn stack(&mut self, amt: u64) -> &mut Self {
unsafe {
BinaryenRustModuleOptionsSetStackAllocation(self.ptr, amt);
}
self
}
/// Flags whether the initial memory should be imported or exported. So far
/// we export it by default.
pub fn import_memory(&mut self, import: bool) -> &mut Self {
unsafe {
BinaryenRustModuleOptionsSetImportMemory(self.ptr, import);
}
self
}
}
impl Drop for ModuleOptions {
fn drop(&mut self) {
unsafe {
BinaryenRustModuleOptionsFree(self.ptr);
}
}
}
enum BinaryenRustModule {}
enum BinaryenRustModuleOptions {}
extern {
fn BinaryenRustModuleCreate(opts: *const BinaryenRustModuleOptions,
assembly: *const libc::c_char)
-> *mut BinaryenRustModule;
fn BinaryenRustModulePtr(module: *const BinaryenRustModule) -> *const u8;
fn BinaryenRustModuleLen(module: *const BinaryenRustModule) -> usize;
fn BinaryenRustModuleFree(module: *mut BinaryenRustModule);
fn BinaryenRustModuleOptionsCreate()
-> *mut BinaryenRustModuleOptions;
fn BinaryenRustModuleOptionsSetDebugInfo(module: *mut BinaryenRustModuleOptions,
debuginfo: bool);
fn BinaryenRustModuleOptionsSetStart(module: *mut BinaryenRustModuleOptions,
start: *const libc::c_char);
fn BinaryenRustModuleOptionsSetStackAllocation(
module: *mut BinaryenRustModuleOptions,
stack: u64,
);
fn BinaryenRustModuleOptionsSetImportMemory(
module: *mut BinaryenRustModuleOptions,
import: bool,
);
fn BinaryenRustModuleOptionsFree(module: *mut BinaryenRustModuleOptions);
}

View File

@ -1606,7 +1606,8 @@ extern "C" {
PositionIndependentExecutable: bool,
FunctionSections: bool,
DataSections: bool,
TrapUnreachable: bool)
TrapUnreachable: bool,
Singlethread: bool)
-> TargetMachineRef;
pub fn LLVMRustDisposeTargetMachine(T: TargetMachineRef);
pub fn LLVMRustAddAnalysisPasses(T: TargetMachineRef, PM: PassManagerRef, M: ModuleRef);

View File

@ -11,16 +11,17 @@ test = false
[dependencies]
bitflags = "1.0"
num_cpus = "1.0"
flate2 = "0.2"
jobserver = "0.1.5"
log = "0.3"
num_cpus = "1.0"
owning_ref = "0.3.3"
rustc-demangle = "0.1.4"
rustc = { path = "../librustc" }
rustc-demangle = "0.1.4"
rustc_allocator = { path = "../librustc_allocator" }
rustc_apfloat = { path = "../librustc_apfloat" }
rustc_back = { path = "../librustc_back" }
rustc_binaryen = { path = "../librustc_binaryen" }
rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }

View File

@ -27,7 +27,7 @@ use rustc::util::common::time;
use rustc::util::fs::fix_windows_verbatim_for_gcc;
use rustc::hir::def_id::CrateNum;
use rustc_back::tempdir::TempDir;
use rustc_back::{PanicStrategy, RelroLevel};
use rustc_back::{PanicStrategy, RelroLevel, LinkerFlavor};
use context::get_reloc_model;
use llvm;
@ -245,12 +245,12 @@ pub fn each_linked_rlib(sess: &Session,
/// It's unusual for a crate to not participate in LTO. Typically only
/// compiler-specific and unstable crates have a reason to not participate in
/// LTO.
pub fn ignored_for_lto(info: &CrateInfo, cnum: CrateNum) -> bool {
// `#![no_builtins]` crates don't participate in LTO because the state
// of builtins gets messed up (our crate isn't tagged with no builtins).
// Similarly `#![compiler_builtins]` doesn't participate because we want
// those builtins!
info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)
pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
// If our target enables builtin function lowering in LLVM then the
// crates providing these functions don't participate in LTO (e.g.
// no_builtins or compiler builtins crates).
!sess.target.target.options.no_builtins &&
(info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum))
}
fn link_binary_output(sess: &Session,
@ -488,7 +488,7 @@ fn link_staticlib(sess: &Session,
});
ab.add_rlib(path,
&name.as_str(),
sess.lto() && !ignored_for_lto(&trans.crate_info, cnum),
sess.lto() && !ignored_for_lto(sess, &trans.crate_info, cnum),
skip_object_files).unwrap();
all_native_libs.extend(trans.crate_info.native_libraries[&cnum].iter().cloned());
@ -548,6 +548,11 @@ fn link_natively(sess: &Session,
info!("preparing {:?} to {:?}", crate_type, out_filename);
let flavor = sess.linker_flavor();
// The "binaryen linker" is massively special, so skip everything below.
if flavor == LinkerFlavor::Binaryen {
return link_binaryen(sess, crate_type, out_filename, trans, tmpdir);
}
// The invocations of cc share some flags across platforms
let (pname, mut cmd, envs) = get_linker(sess);
// This will set PATH on windows
@ -1176,7 +1181,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)
});
if (!sess.lto() || ignored_for_lto(&trans.crate_info, cnum)) &&
if (!sess.lto() || ignored_for_lto(sess, &trans.crate_info, cnum)) &&
crate_type != config::CrateTypeDylib &&
!skip_native {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath));
@ -1229,8 +1234,10 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
// file, then we don't need the object file as it's part of the
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
// though, so we let that object file slide.
let skip_because_lto = sess.lto() && is_rust_object &&
!trans.crate_info.is_no_builtins.contains(&cnum);
let skip_because_lto = sess.lto() &&
is_rust_object &&
(sess.target.target.options.no_builtins ||
!trans.crate_info.is_no_builtins.contains(&cnum));
if skip_because_cfg_say_so || skip_because_lto {
archive.remove_file(&f);
@ -1345,3 +1352,30 @@ fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool {
None => true,
}
}
/// For now "linking with binaryen" is just "move the one module we generated in
/// the backend to the final output"
///
/// That is, all the heavy lifting happens during the `back::write` phase. Here
/// we just clean up after that.
///
/// Note that this is super temporary and "will not survive the night", this is
/// guaranteed to get removed as soon as a linker for wasm exists. This should
/// not be used for anything other than wasm.
fn link_binaryen(sess: &Session,
_crate_type: config::CrateType,
out_filename: &Path,
trans: &CrateTranslation,
_tmpdir: &Path) {
assert!(trans.allocator_module.is_none());
assert_eq!(trans.modules.len(), 1);
let object = trans.modules[0].object.as_ref().expect("object must exist");
let res = fs::hard_link(object, out_filename)
.or_else(|_| fs::copy(object, out_filename).map(|_| ()));
if let Err(e) = res {
sess.fatal(&format!("failed to create `{}`: {}",
out_filename.display(),
e));
}
}

View File

@ -77,6 +77,9 @@ impl LinkerInfo {
is_ld: true,
}) as Box<Linker>
}
LinkerFlavor::Binaryen => {
panic!("can't instantiate binaryen linker")
}
}
}
}

View File

@ -21,6 +21,7 @@ use rustc::ty::TyCtxt;
use rustc::ty::maps::Providers;
use rustc::util::nodemap::FxHashMap;
use rustc_allocator::ALLOCATOR_METHODS;
use rustc_back::LinkerFlavor;
use syntax::attr;
pub type ExportedSymbols = FxHashMap<
@ -154,12 +155,26 @@ pub fn provide_extern(providers: &mut Providers) {
let special_runtime_crate =
tcx.is_panic_runtime(cnum) || tcx.is_compiler_builtins(cnum);
// Dealing with compiler-builtins and wasm right now is super janky.
// There's no linker! As a result we need all of the compiler-builtins
// exported symbols to make their way through all the way to the end of
// compilation. We want to make sure that LLVM doesn't remove them as
// well because we may or may not need them in the final output
// artifact. For now just force them to always get exported at the C
// layer, and we'll worry about gc'ing them later.
let compiler_builtins_and_binaryen =
tcx.is_compiler_builtins(cnum) &&
tcx.sess.linker_flavor() == LinkerFlavor::Binaryen;
let mut crate_exports: Vec<_> = tcx
.exported_symbol_ids(cnum)
.iter()
.map(|&def_id| {
let name = tcx.symbol_name(Instance::mono(tcx, def_id));
let export_level = if special_runtime_crate {
let export_level = if compiler_builtins_and_binaryen &&
tcx.contains_extern_indicator(def_id) {
SymbolExportLevel::C
} else if special_runtime_crate {
// We can probably do better here by just ensuring that
// it has hidden visibility rather than public
// visibility, as this is primarily here to ensure it's

View File

@ -22,6 +22,7 @@ use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Pas
AllPasses, Sanitizer};
use rustc::session::Session;
use rustc::util::nodemap::FxHashMap;
use rustc_back::LinkerFlavor;
use time_graph::{self, TimeGraph, Timeline};
use llvm;
use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef};
@ -47,7 +48,7 @@ use std::any::Any;
use std::ffi::{CString, CStr};
use std::fs::{self, File};
use std::io;
use std::io::Write;
use std::io::{Read, Write};
use std::mem;
use std::path::{Path, PathBuf};
use std::str;
@ -186,6 +187,8 @@ pub fn target_machine_factory(sess: &Session)
}
};
let singlethread = sess.target.target.options.singlethread;
let triple = &sess.target.target.llvm_target;
let triple = CString::new(triple.as_bytes()).unwrap();
@ -210,6 +213,7 @@ pub fn target_machine_factory(sess: &Session)
ffunction_sections,
fdata_sections,
trap_unreachable,
singlethread,
)
};
@ -287,7 +291,7 @@ impl ModuleConfig {
fn set_flags(&mut self, sess: &Session, no_builtins: bool) {
self.no_verify = sess.no_verify();
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = no_builtins;
self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
self.time_passes = sess.time_passes();
self.inline_threshold = sess.opts.cg.inline_threshold;
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
@ -330,6 +334,9 @@ pub struct CodegenContext {
pub tm_factory: Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>,
pub msvc_imps_needed: bool,
pub target_pointer_width: String,
binaryen_linker: bool,
debuginfo: config::DebugInfoLevel,
wasm_import_memory: bool,
// Number of cgus excluding the allocator/metadata modules
pub total_cgus: usize,
@ -625,14 +632,21 @@ unsafe fn codegen(cgcx: &CodegenContext,
f(cpm)
}
// If we're going to generate wasm code from the assembly that llvm
// generates then we'll be transitively affecting a ton of options below.
// This only happens on the wasm target now.
let asm2wasm = cgcx.binaryen_linker &&
!cgcx.crate_types.contains(&config::CrateTypeRlib) &&
mtrans.kind == ModuleKind::Regular;
// Change what we write and cleanup based on whether obj files are
// just llvm bitcode. In that case write bitcode, and possibly
// delete the bitcode if it wasn't requested. Don't generate the
// machine code, instead copy the .o file from the .bc
let write_bc = config.emit_bc || config.obj_is_bitcode;
let rm_bc = !config.emit_bc && config.obj_is_bitcode;
let write_obj = config.emit_obj && !config.obj_is_bitcode;
let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode;
let write_bc = config.emit_bc || (config.obj_is_bitcode && !asm2wasm);
let rm_bc = !config.emit_bc && config.obj_is_bitcode && !asm2wasm;
let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm2wasm;
let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode && !asm2wasm;
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
@ -711,7 +725,7 @@ unsafe fn codegen(cgcx: &CodegenContext,
timeline.record("ir");
}
if config.emit_asm {
if config.emit_asm || (asm2wasm && config.emit_obj) {
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
// We can't use the same module for asm and binary output, because that triggers
@ -732,7 +746,15 @@ unsafe fn codegen(cgcx: &CodegenContext,
timeline.record("asm");
}
if write_obj {
if asm2wasm && config.emit_obj {
let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
binaryen_assemble(cgcx, diag_handler, &assembly, &obj_out);
timeline.record("binaryen");
if !config.emit_asm {
drop(fs::remove_file(&assembly));
}
} else if write_obj {
with_codegen(tm, llmod, config.no_builtins, |cpm| {
write_output_file(diag_handler, tm, cpm, llmod, &obj_out,
llvm::FileType::ObjectFile)
@ -764,6 +786,44 @@ unsafe fn codegen(cgcx: &CodegenContext,
&cgcx.output_filenames))
}
/// Translates the LLVM-generated `assembly` on the filesystem into a wasm
/// module using binaryen, placing the output at `object`.
///
/// In this case the "object" is actually a full and complete wasm module. We
/// won't actually be doing anything else to the output for now. This is all
/// pretty janky and will get removed as soon as a linker for wasm exists.
fn binaryen_assemble(cgcx: &CodegenContext,
handler: &Handler,
assembly: &Path,
object: &Path) {
use rustc_binaryen::{Module, ModuleOptions};
let input = File::open(&assembly).and_then(|mut f| {
let mut contents = Vec::new();
f.read_to_end(&mut contents)?;
Ok(CString::new(contents)?)
});
let mut options = ModuleOptions::new();
if cgcx.debuginfo != config::NoDebugInfo {
options.debuginfo(true);
}
if cgcx.crate_types.contains(&config::CrateTypeExecutable) {
options.start("main");
}
options.stack(1024 * 1024);
options.import_memory(cgcx.wasm_import_memory);
let assembled = input.and_then(|input| {
Module::new(&input, &options)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
});
let err = assembled.and_then(|binary| {
File::create(&object).and_then(|mut f| f.write_all(binary.data()))
});
if let Err(e) = err {
handler.err(&format!("failed to run binaryen assembler: {}", e));
}
}
pub struct CompiledModules {
pub modules: Vec<CompiledModule>,
pub metadata_module: CompiledModule,
@ -1318,17 +1378,33 @@ fn start_executing_work(tcx: TyCtxt,
let mut each_linked_rlib_for_lto = Vec::new();
drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| {
if link::ignored_for_lto(crate_info, cnum) {
if link::ignored_for_lto(sess, crate_info, cnum) {
return
}
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
}));
let crate_types = sess.crate_types.borrow();
let only_rlib = crate_types.len() == 1 &&
crate_types[0] == config::CrateTypeRlib;
let wasm_import_memory =
attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory");
let cgcx = CodegenContext {
crate_types: sess.crate_types.borrow().clone(),
each_linked_rlib_for_lto,
lto: sess.lto(),
thinlto: sess.opts.debugging_opts.thinlto,
// If we're only building an rlibc then allow the LTO flag to be passed
// but don't actually do anything, the full LTO will happen later
lto: sess.lto() && !only_rlib,
// Enable ThinLTO if requested, but only if the target we're compiling
// for doesn't require full LTO. Some targets require one LLVM module
// (they effectively don't have a linker) so it's up to us to use LTO to
// link everything together.
thinlto: sess.opts.debugging_opts.thinlto &&
!sess.target.target.options.requires_lto,
no_landing_pads: sess.no_landing_pads(),
save_temps: sess.opts.cg.save_temps,
opts: Arc::new(sess.opts.clone()),
@ -1349,6 +1425,9 @@ fn start_executing_work(tcx: TyCtxt,
total_cgus,
msvc_imps_needed: msvc_imps_needed(tcx),
target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen,
debuginfo: tcx.sess.opts.debuginfo,
wasm_import_memory: wasm_import_memory,
};
// This is the "main loop" of parallel work happening for parallel codegen.

View File

@ -43,18 +43,19 @@ extern crate flate2;
extern crate libc;
extern crate owning_ref;
#[macro_use] extern crate rustc;
extern crate jobserver;
extern crate num_cpus;
extern crate rustc_allocator;
extern crate rustc_apfloat;
extern crate rustc_back;
extern crate rustc_binaryen;
extern crate rustc_const_math;
extern crate rustc_data_structures;
extern crate rustc_demangle;
extern crate rustc_incremental;
extern crate rustc_llvm as llvm;
extern crate rustc_platform_intrinsics as intrinsics;
extern crate rustc_const_math;
extern crate rustc_trans_utils;
extern crate rustc_demangle;
extern crate jobserver;
extern crate num_cpus;
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;

View File

@ -163,15 +163,30 @@ pub fn default_output_for_target(sess: &Session) -> config::CrateType {
/// Checks if target supports crate_type as output
pub fn invalid_output_for_target(sess: &Session,
crate_type: config::CrateType) -> bool {
match (sess.target.target.options.dynamic_linking,
sess.target.target.options.executables, crate_type) {
(false, _, config::CrateTypeCdylib) |
(false, _, config::CrateTypeDylib) |
(false, _, config::CrateTypeProcMacro) => true,
(true, _, config::CrateTypeCdylib) |
(true, _, config::CrateTypeDylib) => sess.crt_static() &&
!sess.target.target.options.crt_static_allows_dylibs,
(_, false, config::CrateTypeExecutable) => true,
_ => false
match crate_type {
config::CrateTypeCdylib |
config::CrateTypeDylib |
config::CrateTypeProcMacro => {
if !sess.target.target.options.dynamic_linking {
return true
}
if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs {
return true
}
}
_ => {}
}
if sess.target.target.options.only_cdylib {
match crate_type {
config::CrateTypeProcMacro | config::CrateTypeDylib => return true,
_ => {}
}
}
if !sess.target.target.options.executables {
if crate_type == config::CrateTypeExecutable {
return true
}
}
false
}

View File

@ -19,8 +19,12 @@ use build_helper::{run, native_lib_boilerplate, BuildExpectation};
fn main() {
let target = env::var("TARGET").expect("TARGET was not set");
let host = env::var("HOST").expect("HOST was not set");
if cfg!(feature = "backtrace") && !target.contains("msvc") &&
!target.contains("emscripten") && !target.contains("fuchsia") {
if cfg!(feature = "backtrace") &&
!target.contains("msvc") &&
!target.contains("emscripten") &&
!target.contains("fuchsia") &&
!target.contains("wasm32")
{
let _ = build_libbacktrace(&host, &target);
}

View File

@ -26,7 +26,6 @@ use num::FpCategory;
#[cfg(not(test))]
use sys::cmath;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::f32::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON};
#[stable(feature = "rust1", since = "1.0.0")]

View File

@ -663,3 +663,39 @@ pub mod builtin {
#[macro_export]
macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) }
}
/// A macro for defining #[cfg] if-else statements.
///
/// This is similar to the `if/elif` C preprocessor macro by allowing definition
/// of a cascade of `#[cfg]` cases, emitting the implementation which matches
/// first.
///
/// This allows you to conveniently provide a long list #[cfg]'d blocks of code
/// without having to rewrite each clause multiple times.
macro_rules! cfg_if {
($(
if #[cfg($($meta:meta),*)] { $($it:item)* }
) else * else {
$($it2:item)*
}) => {
__cfg_if_items! {
() ;
$( ( ($($meta),*) ($($it)*) ), )*
( () ($($it2)*) ),
}
}
}
macro_rules! __cfg_if_items {
(($($not:meta,)*) ; ) => {};
(($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
__cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* }
__cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
}
}
macro_rules! __cfg_if_apply {
($m:meta, $($it:item)*) => {
$(#[$m] $it)*
}
}

View File

@ -46,6 +46,9 @@ mod imp;
#[path = "redox/mod.rs"]
mod imp;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[path = "wasm/mod.rs"]
mod imp;
// Import essential modules from both platforms when documenting.

View File

@ -0,0 +1,90 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use ffi::OsString;
use marker::PhantomData;
use mem;
use vec;
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
// On wasm these should always be null, so there's nothing for us to do here
}
pub unsafe fn cleanup() {
}
pub fn args() -> Args {
// When the runtime debugging is enabled we'll link to some extra runtime
// functions to actually implement this. These are for now just implemented
// in a node.js script but they're off by default as they're sort of weird
// in a web-wasm world.
if !super::DEBUG {
return Args {
iter: Vec::new().into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}
// You'll find the definitions of these in `src/etc/wasm32-shim.js`. These
// are just meant for debugging and should not be relied on.
extern {
fn rust_wasm_args_count() -> usize;
fn rust_wasm_args_arg_size(a: usize) -> usize;
fn rust_wasm_args_arg_fill(a: usize, ptr: *mut u8);
}
unsafe {
let cnt = rust_wasm_args_count();
let mut v = Vec::with_capacity(cnt);
for i in 0..cnt {
let n = rust_wasm_args_arg_size(i);
let mut data = vec![0; n];
rust_wasm_args_arg_fill(i, data.as_mut_ptr());
v.push(mem::transmute::<Vec<u8>, OsString>(data));
}
Args {
iter: v.into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}
}
pub struct Args {
iter: vec::IntoIter<OsString>,
_dont_send_or_sync_me: PhantomData<*mut ()>,
}
impl Args {
pub fn inner_debug(&self) -> &[OsString] {
self.iter.as_slice()
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize {
self.iter.len()
}
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.iter.next_back()
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use io;
use sys::unsupported;
use sys_common::backtrace::Frame;
pub struct BacktraceContext;
pub fn unwind_backtrace(_frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
unsupported()
}
pub fn resolve_symname<F>(_frame: Frame,
_callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
unsupported()
}
pub fn foreach_symbol_fileline<F>(_: Frame,
_: F,
_: &BacktraceContext) -> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
unsupported()
}

View File

@ -0,0 +1,119 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[inline]
pub unsafe fn cbrtf(n: f32) -> f32 {
f64::cbrt(n as f64) as f32
}
#[inline]
pub unsafe fn expm1f(n: f32) -> f32 {
f64::exp_m1(n as f64) as f32
}
#[inline]
#[allow(deprecated)]
pub unsafe fn fdimf(a: f32, b: f32) -> f32 {
f64::abs_sub(a as f64, b as f64) as f32
}
#[inline]
pub unsafe fn log1pf(n: f32) -> f32 {
f64::ln_1p(n as f64) as f32
}
#[inline]
pub unsafe fn hypotf(x: f32, y: f32) -> f32 {
f64::hypot(x as f64, y as f64) as f32
}
#[inline]
pub unsafe fn acosf(n: f32) -> f32 {
f64::acos(n as f64) as f32
}
#[inline]
pub unsafe fn asinf(n: f32) -> f32 {
f64::asin(n as f64) as f32
}
#[inline]
pub unsafe fn atan2f(n: f32, b: f32) -> f32 {
f64::atan2(n as f64, b as f64) as f32
}
#[inline]
pub unsafe fn atanf(n: f32) -> f32 {
f64::atan(n as f64) as f32
}
#[inline]
pub unsafe fn coshf(n: f32) -> f32 {
f64::cosh(n as f64) as f32
}
#[inline]
pub unsafe fn sinhf(n: f32) -> f32 {
f64::sinh(n as f64) as f32
}
#[inline]
pub unsafe fn tanf(n: f32) -> f32 {
f64::tan(n as f64) as f32
}
#[inline]
pub unsafe fn tanhf(n: f32) -> f32 {
f64::tanh(n as f64) as f32
}
// Right now all these functions, the f64 version of the functions above, all
// shell out to random names. These names aren't actually defined anywhere, per
// se, but we need this to compile somehow.
//
// The idea with this is that when you're using wasm then, for now, we have no
// way of providing an implementation of these which delegates to a "correct"
// implementation. For example most wasm applications probably just want to
// delegate to the javascript `Math` object and its related functions, but wasm
// doesn't currently have the ability to seamlessly do that (when you
// instantiate a module you have to set that up).
//
// As a result these are just defined here with "hopefully helpful" names. The
// symbols won't ever be needed or show up unless these functions are called,
// and hopefully when they're called the errors are self-explanatory enough to
// figure out what's going on.
extern {
#[link_name = "Math_acos"]
pub fn acos(n: f64) -> f64;
#[link_name = "Math_asin"]
pub fn asin(n: f64) -> f64;
#[link_name = "Math_atan"]
pub fn atan(n: f64) -> f64;
#[link_name = "Math_atan2"]
pub fn atan2(a: f64, b: f64) -> f64;
#[link_name = "Math_cbrt"]
pub fn cbrt(n: f64) -> f64;
#[link_name = "Math_cosh"]
pub fn cosh(n: f64) -> f64;
#[link_name = "Math_expm1"]
pub fn expm1(n: f64) -> f64;
pub fn fdim(a: f64, b: f64) -> f64;
#[link_name = "Math_log1p"]
pub fn log1p(n: f64) -> f64;
#[link_name = "Math_sinh"]
pub fn sinh(n: f64) -> f64;
#[link_name = "Math_tan"]
pub fn tan(n: f64) -> f64;
#[link_name = "Math_tanh"]
pub fn tanh(n: f64) -> f64;
#[link_name = "Math_hypot"]
pub fn hypot(x: f64, y: f64) -> f64;
}

View File

@ -0,0 +1,43 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use sys::mutex::Mutex;
use time::Duration;
pub struct Condvar { }
impl Condvar {
pub const fn new() -> Condvar {
Condvar { }
}
#[inline]
pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn notify_one(&self) {
}
#[inline]
pub unsafe fn notify_all(&self) {
}
pub unsafe fn wait(&self, _mutex: &Mutex) {
panic!("can't block with web assembly")
}
pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
panic!("can't block with web assembly");
}
#[inline]
pub unsafe fn destroy(&self) {
}
}

View File

@ -0,0 +1,19 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub mod os {
pub const FAMILY: &'static str = "";
pub const OS: &'static str = "";
pub const DLL_PREFIX: &'static str = "";
pub const DLL_SUFFIX: &'static str = ".wasm";
pub const DLL_EXTENSION: &'static str = "wasm";
pub const EXE_SUFFIX: &'static str = ".wasm";
pub const EXE_EXTENSION: &'static str = "wasm";
}

304
src/libstd/sys/wasm/fs.rs Normal file
View File

@ -0,0 +1,304 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use ffi::OsString;
use fmt;
use hash::{Hash, Hasher};
use io::{self, SeekFrom};
use path::{Path, PathBuf};
use sys::time::SystemTime;
use sys::{unsupported, Void};
pub struct File(Void);
pub struct FileAttr(Void);
pub struct ReadDir(Void);
pub struct DirEntry(Void);
#[derive(Clone, Debug)]
pub struct OpenOptions { }
pub struct FilePermissions(Void);
pub struct FileType(Void);
#[derive(Debug)]
pub struct DirBuilder { }
impl FileAttr {
pub fn size(&self) -> u64 {
match self.0 {}
}
pub fn perm(&self) -> FilePermissions {
match self.0 {}
}
pub fn file_type(&self) -> FileType {
match self.0 {}
}
pub fn modified(&self) -> io::Result<SystemTime> {
match self.0 {}
}
pub fn accessed(&self) -> io::Result<SystemTime> {
match self.0 {}
}
pub fn created(&self) -> io::Result<SystemTime> {
match self.0 {}
}
}
impl Clone for FileAttr {
fn clone(&self) -> FileAttr {
match self.0 {}
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
match self.0 {}
}
pub fn set_readonly(&mut self, _readonly: bool) {
match self.0 {}
}
}
impl Clone for FilePermissions {
fn clone(&self) -> FilePermissions {
match self.0 {}
}
}
impl PartialEq for FilePermissions {
fn eq(&self, _other: &FilePermissions) -> bool {
match self.0 {}
}
}
impl Eq for FilePermissions {
}
impl fmt::Debug for FilePermissions {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
impl FileType {
pub fn is_dir(&self) -> bool {
match self.0 {}
}
pub fn is_file(&self) -> bool {
match self.0 {}
}
pub fn is_symlink(&self) -> bool {
match self.0 {}
}
}
impl Clone for FileType {
fn clone(&self) -> FileType {
match self.0 {}
}
}
impl Copy for FileType {}
impl PartialEq for FileType {
fn eq(&self, _other: &FileType) -> bool {
match self.0 {}
}
}
impl Eq for FileType {
}
impl Hash for FileType {
fn hash<H: Hasher>(&self, _h: &mut H) {
match self.0 {}
}
}
impl fmt::Debug for FileType {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
match self.0 {}
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
match self.0 {}
}
pub fn file_name(&self) -> OsString {
match self.0 {}
}
pub fn metadata(&self) -> io::Result<FileAttr> {
match self.0 {}
}
pub fn file_type(&self) -> io::Result<FileType> {
match self.0 {}
}
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions { }
}
pub fn read(&mut self, _read: bool) { }
pub fn write(&mut self, _write: bool) { }
pub fn append(&mut self, _append: bool) { }
pub fn truncate(&mut self, _truncate: bool) { }
pub fn create(&mut self, _create: bool) { }
pub fn create_new(&mut self, _create_new: bool) { }
}
impl File {
pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
unsupported()
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
match self.0 {}
}
pub fn fsync(&self) -> io::Result<()> {
match self.0 {}
}
pub fn datasync(&self) -> io::Result<()> {
match self.0 {}
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
match self.0 {}
}
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn flush(&self) -> io::Result<()> {
match self.0 {}
}
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
match self.0 {}
}
pub fn duplicate(&self) -> io::Result<File> {
match self.0 {}
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
match self.0 {}
}
pub fn diverge(&self) -> ! {
match self.0 {}
}
}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder { }
}
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
unsupported()
}
}
impl fmt::Debug for File {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
unsupported()
}
pub fn unlink(_p: &Path) -> io::Result<()> {
unsupported()
}
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
unsupported()
}
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
match perm.0 {}
}
pub fn rmdir(_p: &Path) -> io::Result<()> {
unsupported()
}
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
unsupported()
}
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
unsupported()
}
pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
unsupported()
}
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
unsupported()
}
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
unsupported()
}
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
unsupported()
}

View File

@ -0,0 +1,11 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use sys_common::memchr::fallback::{memchr, memrchr};

104
src/libstd/sys/wasm/mod.rs Normal file
View File

@ -0,0 +1,104 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! System bindings for the wasm/web platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
//! wasm, so we have no runtime here.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This
//! will likely change over time.
//!
//! Currently all functions here are basically stubs that immediately return
//! errors. The hope is that with a portability lint we can turn actually just
//! remove all this and just omit parts of the standard library if we're
//! compiling for wasm. That way it's a compile time error for something that's
//! guaranteed to be a runtime error!
use io;
use os::raw::c_char;
// Right now the wasm backend doesn't even have the ability to print to the
// console by default. Wasm can't import anything from JS! (you have to
// explicitly provide it).
//
// Sometimes that's a real bummer, though, so this flag can be set to `true` to
// enable calling various shims defined in `src/etc/wasm32-shim.js` which should
// help receive debug output and see what's going on. In general this flag
// currently controls "will we call out to our own defined shims in node.js",
// and this flag should always be `false` for release builds.
const DEBUG: bool = false;
pub mod args;
pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod fs;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rwlock;
pub mod stack_overflow;
pub mod thread;
pub mod thread_local;
pub mod time;
pub mod stdio;
#[cfg(not(test))]
pub fn init() {
}
pub fn unsupported<T>() -> io::Result<T> {
Err(unsupported_err())
}
pub fn unsupported_err() -> io::Error {
io::Error::new(io::ErrorKind::Other,
"operation not supported on wasm yet")
}
pub fn decode_error_kind(_code: i32) -> io::ErrorKind {
io::ErrorKind::Other
}
// This enum is used as the storage for a bunch of types which can't actually
// exist.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum Void {}
pub unsafe fn strlen(mut s: *const c_char) -> usize {
let mut n = 0;
while *s != 0 {
n += 1;
s = s.offset(1);
}
return n
}
pub unsafe fn abort_internal() -> ! {
::intrinsics::abort();
}
// We don't have randomness yet, but I totally used a random number generator to
// generate these numbers.
//
// More seriously though this is just for DOS protection in hash maps. It's ok
// if we don't do that on wasm just yet.
pub fn hashmap_random_keys() -> (u64, u64) {
(1, 2)
}

View File

@ -0,0 +1,79 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cell::UnsafeCell;
pub struct Mutex {
locked: UnsafeCell<bool>,
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {} // no threads on wasm
impl Mutex {
pub const fn new() -> Mutex {
Mutex { locked: UnsafeCell::new(false) }
}
#[inline]
pub unsafe fn init(&mut self) {
}
#[inline]
pub unsafe fn lock(&self) {
let locked = self.locked.get();
assert!(!*locked, "cannot recursively acquire mutex");
*locked = true;
}
#[inline]
pub unsafe fn unlock(&self) {
*self.locked.get() = false;
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
let locked = self.locked.get();
if *locked {
false
} else {
*locked = true;
true
}
}
#[inline]
pub unsafe fn destroy(&self) {
}
}
// All empty stubs because wasm has no threads yet, so lock acquisition always
// succeeds.
pub struct ReentrantMutex {
}
impl ReentrantMutex {
pub unsafe fn uninitialized() -> ReentrantMutex {
ReentrantMutex { }
}
pub unsafe fn init(&mut self) {}
pub unsafe fn lock(&self) {}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
true
}
pub unsafe fn unlock(&self) {}
pub unsafe fn destroy(&self) {}
}

337
src/libstd/sys/wasm/net.rs Normal file
View File

@ -0,0 +1,337 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use fmt;
use io;
use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
use time::Duration;
use sys::{unsupported, Void};
pub struct TcpStream(Void);
impl TcpStream {
pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
unsupported()
}
pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
unsupported()
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
match self.0 {}
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
match self.0 {}
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
match self.0 {}
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
match self.0 {}
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
match self.0 {}
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
match self.0 {}
}
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
match self.0 {}
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
match self.0 {}
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn nodelay(&self) -> io::Result<bool> {
match self.0 {}
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
match self.0 {}
}
pub fn ttl(&self) -> io::Result<u32> {
match self.0 {}
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
match self.0 {}
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
pub struct TcpListener(Void);
impl TcpListener {
pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
unsupported()
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
match self.0 {}
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
match self.0 {}
}
pub fn duplicate(&self) -> io::Result<TcpListener> {
match self.0 {}
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
match self.0 {}
}
pub fn ttl(&self) -> io::Result<u32> {
match self.0 {}
}
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn only_v6(&self) -> io::Result<bool> {
match self.0 {}
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
match self.0 {}
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
pub struct UdpSocket(Void);
impl UdpSocket {
pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
unsupported()
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
match self.0 {}
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
match self.0 {}
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
match self.0 {}
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
match self.0 {}
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
match self.0 {}
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
match self.0 {}
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
match self.0 {}
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
match self.0 {}
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
match self.0 {}
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn broadcast(&self) -> io::Result<bool> {
match self.0 {}
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
match self.0 {}
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
match self.0 {}
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
match self.0 {}
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
match self.0 {}
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
-> io::Result<()> {
match self.0 {}
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
-> io::Result<()> {
match self.0 {}
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
-> io::Result<()> {
match self.0 {}
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
-> io::Result<()> {
match self.0 {}
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
match self.0 {}
}
pub fn ttl(&self) -> io::Result<u32> {
match self.0 {}
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
match self.0 {}
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
match self.0 {}
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
match self.0 {}
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
pub struct LookupHost(Void);
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
match self.0 {}
}
}
pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
unsupported()
}
#[allow(bad_style)]
pub mod netc {
pub const AF_INET: u8 = 0;
pub const AF_INET6: u8 = 1;
pub type sa_family_t = u8;
#[derive(Copy, Clone)]
pub struct in_addr {
pub s_addr: u32,
}
#[derive(Copy, Clone)]
pub struct sockaddr_in {
pub sin_family: sa_family_t,
pub sin_port: u16,
pub sin_addr: in_addr,
}
#[derive(Copy, Clone)]
pub struct in6_addr {
pub s6_addr: [u8; 16],
}
#[derive(Copy, Clone)]
pub struct sockaddr_in6 {
pub sin6_family: sa_family_t,
pub sin6_port: u16,
pub sin6_addr: in6_addr,
pub sin6_flowinfo: u32,
pub sin6_scope_id: u32,
}
#[derive(Copy, Clone)]
pub struct sockaddr {
}
pub type socklen_t = usize;
}

136
src/libstd/sys/wasm/os.rs Normal file
View File

@ -0,0 +1,136 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::intrinsics;
use error::Error as StdError;
use ffi::{OsString, OsStr};
use fmt;
use io;
use mem;
use path::{self, PathBuf};
use str;
use sys::{unsupported, Void};
pub fn errno() -> i32 {
0
}
pub fn error_string(_errno: i32) -> String {
format!("operation successful")
}
pub fn getcwd() -> io::Result<PathBuf> {
unsupported()
}
pub fn chdir(_: &path::Path) -> io::Result<()> {
unsupported()
}
pub struct SplitPaths<'a>(&'a Void);
pub fn split_paths(_unparsed: &OsStr) -> SplitPaths {
panic!("unsupported")
}
impl<'a> Iterator for SplitPaths<'a> {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
match *self.0 {}
}
}
#[derive(Debug)]
pub struct JoinPathsError;
pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
where I: Iterator<Item=T>, T: AsRef<OsStr>
{
Err(JoinPathsError)
}
impl fmt::Display for JoinPathsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
"not supported on wasm yet".fmt(f)
}
}
impl StdError for JoinPathsError {
fn description(&self) -> &str {
"not supported on wasm yet"
}
}
pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
pub struct Env(Void);
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
match self.0 {}
}
}
pub fn env() -> Env {
panic!("not supported on web assembly")
}
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
// If we're debugging the runtime then we actually probe node.js to ask for
// the value of environment variables to help provide inputs to programs.
// The `extern` shims here are defined in `src/etc/wasm32-shim.js` and are
// intended for debugging only, you should not rely on them.
if !super::DEBUG {
return Ok(None)
}
extern {
fn rust_wasm_getenv_len(k: *const u8, kl: usize) -> isize;
fn rust_wasm_getenv_data(k: *const u8, kl: usize, v: *mut u8);
}
unsafe {
let k: &[u8] = mem::transmute(k);
let n = rust_wasm_getenv_len(k.as_ptr(), k.len());
if n == -1 {
return Ok(None)
}
let mut data = vec![0; n as usize];
rust_wasm_getenv_data(k.as_ptr(), k.len(), data.as_mut_ptr());
Ok(Some(mem::transmute(data)))
}
}
pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> {
unsupported()
}
pub fn unsetenv(_n: &OsStr) -> io::Result<()> {
unsupported()
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on wasm")
}
pub fn home_dir() -> Option<PathBuf> {
None
}
pub fn exit(_code: i32) -> ! {
unsafe { intrinsics::abort() }
}
pub fn getpid() -> u32 {
panic!("no pids on wasm")
}

View File

@ -0,0 +1,159 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// The underlying OsString/OsStr implementation on Unix systems: just
/// a `Vec<u8>`/`[u8]`.
use borrow::Cow;
use fmt;
use str;
use mem;
use sys_common::{AsInner, IntoInner};
use std_unicode::lossy::Utf8Lossy;
#[derive(Clone, Hash)]
pub struct Buf {
pub inner: Vec<u8>
}
pub struct Slice {
pub inner: [u8]
}
impl fmt::Debug for Slice {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
}
}
impl fmt::Display for Slice {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
}
}
impl fmt::Debug for Buf {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_slice(), formatter)
}
}
impl fmt::Display for Buf {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_slice(), formatter)
}
}
impl IntoInner<Vec<u8>> for Buf {
fn into_inner(self) -> Vec<u8> {
self.inner
}
}
impl AsInner<[u8]> for Buf {
fn as_inner(&self) -> &[u8] {
&self.inner
}
}
impl Buf {
pub fn from_string(s: String) -> Buf {
Buf { inner: s.into_bytes() }
}
#[inline]
pub fn with_capacity(capacity: usize) -> Buf {
Buf {
inner: Vec::with_capacity(capacity)
}
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear()
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional)
}
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.inner.reserve_exact(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.inner.shrink_to_fit()
}
pub fn as_slice(&self) -> &Slice {
unsafe { mem::transmute(&*self.inner) }
}
pub fn into_string(self) -> Result<String, Buf> {
String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
}
pub fn push_slice(&mut self, s: &Slice) {
self.inner.extend_from_slice(&s.inner)
}
#[inline]
pub fn into_box(self) -> Box<Slice> {
unsafe { mem::transmute(self.inner.into_boxed_slice()) }
}
#[inline]
pub fn from_box(boxed: Box<Slice>) -> Buf {
let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
Buf { inner: inner.into_vec() }
}
}
impl Slice {
fn from_u8_slice(s: &[u8]) -> &Slice {
unsafe { mem::transmute(s) }
}
pub fn from_str(s: &str) -> &Slice {
Slice::from_u8_slice(s.as_bytes())
}
pub fn to_str(&self) -> Option<&str> {
str::from_utf8(&self.inner).ok()
}
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(&self.inner)
}
pub fn to_owned(&self) -> Buf {
Buf { inner: self.inner.to_vec() }
}
#[inline]
pub fn into_box(&self) -> Box<Slice> {
let boxed: Box<[u8]> = self.inner.into();
unsafe { mem::transmute(boxed) }
}
pub fn empty_box() -> Box<Slice> {
let boxed: Box<[u8]> = Default::default();
unsafe { mem::transmute(boxed) }
}
}

View File

@ -0,0 +1,29 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use path::Prefix;
use ffi::OsStr;
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/'
}
#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}
pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
None
}
pub const MAIN_SEP_STR: &'static str = "/";
pub const MAIN_SEP: char = '/';

View File

@ -0,0 +1,35 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use io;
use sys::Void;
pub struct AnonPipe(Void);
impl AnonPipe {
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn diverge(&self) -> ! {
match self.0 {}
}
}
pub fn read2(p1: AnonPipe,
_v1: &mut Vec<u8>,
_p2: AnonPipe,
_v2: &mut Vec<u8>) -> io::Result<()> {
match p1.0 {}
}

View File

@ -0,0 +1,151 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use ffi::OsStr;
use fmt;
use io;
use sys::fs::File;
use sys::pipe::AnonPipe;
use sys::{unsupported, Void};
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
pub struct Command {
}
// passed back to std::process with the pipes connected to the child, if any
// were requested
pub struct StdioPipes {
pub stdin: Option<AnonPipe>,
pub stdout: Option<AnonPipe>,
pub stderr: Option<AnonPipe>,
}
pub enum Stdio {
Inherit,
Null,
MakePipe,
}
impl Command {
pub fn new(_program: &OsStr) -> Command {
Command {}
}
pub fn arg(&mut self, _arg: &OsStr) {
}
pub fn env(&mut self, _key: &OsStr, _val: &OsStr) {
}
pub fn env_remove(&mut self, _key: &OsStr) {
}
pub fn env_clear(&mut self) {
}
pub fn cwd(&mut self, _dir: &OsStr) {
}
pub fn stdin(&mut self, _stdin: Stdio) {
}
pub fn stdout(&mut self, _stdout: Stdio) {
}
pub fn stderr(&mut self, _stderr: Stdio) {
}
pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
unsupported()
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
pipe.diverge()
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
file.diverge()
}
}
impl fmt::Debug for Command {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
pub struct ExitStatus(Void);
impl ExitStatus {
pub fn success(&self) -> bool {
match self.0 {}
}
pub fn code(&self) -> Option<i32> {
match self.0 {}
}
}
impl Clone for ExitStatus {
fn clone(&self) -> ExitStatus {
match self.0 {}
}
}
impl Copy for ExitStatus {}
impl PartialEq for ExitStatus {
fn eq(&self, _other: &ExitStatus) -> bool {
match self.0 {}
}
}
impl Eq for ExitStatus {
}
impl fmt::Debug for ExitStatus {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {}
}
}
pub struct Process(Void);
impl Process {
pub fn id(&self) -> u32 {
match self.0 {}
}
pub fn kill(&mut self) -> io::Result<()> {
match self.0 {}
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
match self.0 {}
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
match self.0 {}
}
}

View File

@ -0,0 +1,82 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cell::UnsafeCell;
pub struct RWLock {
mode: UnsafeCell<isize>,
}
unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {} // no threads on wasm
impl RWLock {
pub const fn new() -> RWLock {
RWLock {
mode: UnsafeCell::new(0),
}
}
#[inline]
pub unsafe fn read(&self) {
let mode = self.mode.get();
if *mode >= 0 {
*mode += 1;
} else {
panic!("rwlock locked for writing");
}
}
#[inline]
pub unsafe fn try_read(&self) -> bool {
let mode = self.mode.get();
if *mode >= 0 {
*mode += 1;
true
} else {
false
}
}
#[inline]
pub unsafe fn write(&self) {
let mode = self.mode.get();
if *mode == 0 {
*mode = -1;
} else {
panic!("rwlock locked for reading")
}
}
#[inline]
pub unsafe fn try_write(&self) -> bool {
let mode = self.mode.get();
if *mode == 0 {
*mode = -1;
true
} else {
false
}
}
#[inline]
pub unsafe fn read_unlock(&self) {
*self.mode.get() -= 1;
}
#[inline]
pub unsafe fn write_unlock(&self) {
*self.mode.get() += 1;
}
#[inline]
pub unsafe fn destroy(&self) {
}
}

View File

@ -0,0 +1,23 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct Handler;
impl Handler {
pub unsafe fn new() -> Handler {
Handler
}
}
pub unsafe fn init() {
}
pub unsafe fn cleanup() {
}

View File

@ -0,0 +1,92 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use io;
use sys::{Void, unsupported};
pub struct Stdin(Void);
pub struct Stdout;
pub struct Stderr;
impl Stdin {
pub fn new() -> io::Result<Stdin> {
unsupported()
}
pub fn read(&self, _data: &mut [u8]) -> io::Result<usize> {
match self.0 {}
}
}
impl Stdout {
pub fn new() -> io::Result<Stdout> {
Ok(Stdout)
}
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
// If runtime debugging is enabled at compile time we'll invoke some
// runtime functions that are defined in our src/etc/wasm32-shim.js
// debugging script. Note that this ffi function call is intended
// *purely* for debugging only and should not be relied upon.
if !super::DEBUG {
return unsupported()
}
extern {
fn rust_wasm_write_stdout(data: *const u8, len: usize);
}
unsafe {
rust_wasm_write_stdout(data.as_ptr(), data.len())
}
Ok(data.len())
}
pub fn flush(&self) -> io::Result<()> {
Ok(())
}
}
impl Stderr {
pub fn new() -> io::Result<Stderr> {
Ok(Stderr)
}
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
// See comments in stdout for what's going on here.
if !super::DEBUG {
return unsupported()
}
extern {
fn rust_wasm_write_stderr(data: *const u8, len: usize);
}
unsafe {
rust_wasm_write_stderr(data.as_ptr(), data.len())
}
Ok(data.len())
}
pub fn flush(&self) -> io::Result<()> {
Ok(())
}
}
impl io::Write for Stderr {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
(&*self).write(data)
}
fn flush(&mut self) -> io::Result<()> {
(&*self).flush()
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub fn is_ebadf(_err: &io::Error) -> bool {
true
}

View File

@ -0,0 +1,48 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use alloc::boxed::FnBox;
use ffi::CStr;
use io;
use sys::{unsupported, Void};
use time::Duration;
pub struct Thread(Void);
pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
impl Thread {
pub unsafe fn new<'a>(_stack: usize, _p: Box<FnBox() + 'a>)
-> io::Result<Thread>
{
unsupported()
}
pub fn yield_now() {
// do nothing
}
pub fn set_name(_name: &CStr) {
// nope
}
pub fn sleep(_dur: Duration) {
panic!("can't sleep");
}
pub fn join(self) {
match self.0 {}
}
}
pub mod guard {
pub unsafe fn current() -> Option<usize> { None }
pub unsafe fn init() -> Option<usize> { None }
}

View File

@ -0,0 +1,50 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use boxed::Box;
use ptr;
pub type Key = usize;
struct Allocated {
value: *mut u8,
dtor: Option<unsafe extern fn(*mut u8)>,
}
#[inline]
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
Box::into_raw(Box::new(Allocated {
value: ptr::null_mut(),
dtor,
})) as usize
}
#[inline]
pub unsafe fn set(key: Key, value: *mut u8) {
(*(key as *mut Allocated)).value = value;
}
#[inline]
pub unsafe fn get(key: Key) -> *mut u8 {
(*(key as *mut Allocated)).value
}
#[inline]
pub unsafe fn destroy(key: Key) {
let key = Box::from_raw(key as *mut Allocated);
if let Some(f) = key.dtor {
f(key.value);
}
}
#[inline]
pub fn requires_synchronized_create() -> bool {
false
}

View File

@ -0,0 +1,63 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use fmt;
use time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Instant;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SystemTime;
pub const UNIX_EPOCH: SystemTime = SystemTime;
impl Instant {
pub fn now() -> Instant {
panic!("not supported on web assembly");
}
pub fn sub_instant(&self, _other: &Instant) -> Duration {
panic!("can't sub yet");
}
pub fn add_duration(&self, _other: &Duration) -> Instant {
panic!("can't add yet");
}
pub fn sub_duration(&self, _other: &Duration) -> Instant {
panic!("can't sub yet");
}
}
impl SystemTime {
pub fn now() -> SystemTime {
panic!("not supported on web assembly");
}
pub fn sub_time(&self, _other: &SystemTime)
-> Result<Duration, Duration> {
panic!()
}
pub fn add_duration(&self, _other: &Duration) -> SystemTime {
panic!()
}
pub fn sub_duration(&self, _other: &Duration) -> SystemTime {
panic!()
}
}
impl fmt::Debug for SystemTime {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
panic!()
}
}

View File

@ -44,11 +44,15 @@ pub mod thread_local;
pub mod util;
pub mod wtf8;
#[cfg(any(target_os = "redox", target_os = "l4re"))]
pub use sys::net;
#[cfg(not(any(target_os = "redox", target_os = "l4re")))]
pub mod net;
cfg_if! {
if #[cfg(any(target_os = "redox", target_os = "l4re"))] {
pub use sys::net;
} else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
pub use sys::net;
} else {
pub mod net;
}
}
#[cfg(feature = "backtrace")]
#[cfg(any(all(unix, not(target_os = "emscripten")),

View File

@ -8,12 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use env;
use alloc::boxed::FnBox;
use env;
use sync::atomic::{self, Ordering};
use sys::stack_overflow;
use sys::thread as imp;
#[allow(dead_code)]
pub unsafe fn start_thread(main: *mut u8) {
// Next, set up our stack overflow handler which may get triggered if we run
// out of stack.

View File

@ -415,6 +415,9 @@ declare_features! (
// Allow trait methods with arbitrary self types
(active, arbitrary_self_types, "1.23.0", Some(44874)),
// #![wasm_import_memory] attribute
(active, wasm_import_memory, "1.22.0", None),
);
declare_features! (
@ -928,6 +931,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
never be stable",
cfg_fn!(rustc_attrs))),
("wasm_import_memory", Whitelisted, Gated(Stability::Unstable,
"wasm_import_memory",
"wasm_import_memory attribute is currently unstable",
cfg_fn!(wasm_import_memory))),
// Crate level attributes
("crate_name", CrateLevel, Ungated),
("crate_type", CrateLevel, Ungated),

View File

@ -434,7 +434,8 @@ Test Attributes:
// Parses command line arguments into test options
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
let opts = optgroups();
let matches = match opts.parse(&args[1..]) {
let args = args.get(1..).unwrap_or(args);
let matches = match opts.parse(args) {
Ok(m) => m,
Err(f) => return Some(Err(f.to_string())),
};
@ -1034,6 +1035,10 @@ fn stdout_isatty() -> bool {
// FIXME: Implement isatty on Redox
false
}
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
fn stdout_isatty() -> bool {
false
}
#[cfg(unix)]
fn stdout_isatty() -> bool {
unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
@ -1132,45 +1137,47 @@ pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F)
}})
};
while pending > 0 || !remaining.is_empty() {
while pending < concurrency && !remaining.is_empty() {
if concurrency == 1 {
while !remaining.is_empty() {
let test = remaining.pop().unwrap();
if concurrency == 1 {
// We are doing one test at a time so we can print the name
// of the test before we run it. Useful for debugging tests
// that hang forever.
callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
}
let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
running_tests.insert(test.desc.clone(), timeout);
callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
run_test(opts, !opts.run_tests, test, tx.clone());
pending += 1;
let (test, result, stdout) = rx.recv().unwrap();
callback(TeResult(test, result, stdout))?;
}
} else {
while pending > 0 || !remaining.is_empty() {
while pending < concurrency && !remaining.is_empty() {
let test = remaining.pop().unwrap();
let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
running_tests.insert(test.desc.clone(), timeout);
run_test(opts, !opts.run_tests, test, tx.clone());
pending += 1;
}
let mut res;
loop {
if let Some(timeout) = calc_timeout(&running_tests) {
res = rx.recv_timeout(timeout);
for test in get_timed_out_tests(&mut running_tests) {
callback(TeTimeout(test))?;
}
if res != Err(RecvTimeoutError::Timeout) {
let mut res;
loop {
if let Some(timeout) = calc_timeout(&running_tests) {
res = rx.recv_timeout(timeout);
for test in get_timed_out_tests(&mut running_tests) {
callback(TeTimeout(test))?;
}
if res != Err(RecvTimeoutError::Timeout) {
break;
}
} else {
res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
break;
}
} else {
res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
break;
}
}
let (desc, result, stdout) = res.unwrap();
running_tests.remove(&desc);
let (desc, result, stdout) = res.unwrap();
running_tests.remove(&desc);
if concurrency != 1 {
callback(TeWait(desc.clone(), PadNone))?;
callback(TeResult(desc, result, stdout))?;
pending -= 1;
}
callback(TeResult(desc, result, stdout))?;
pending -= 1;
}
if opts.bench_benchmarks {
@ -1235,6 +1242,11 @@ fn get_concurrency() -> usize {
1
}
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
fn num_cpus() -> usize {
1
}
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "ios",
@ -1393,7 +1405,12 @@ pub fn run_test(opts: &TestOpts,
let TestDescAndFn {desc, testfn} = test;
if force_ignore || desc.ignore {
let ignore_because_panic_abort =
cfg!(target_arch = "wasm32") &&
!cfg!(target_os = "emscripten") &&
desc.should_panic != ShouldPanic::No;
if force_ignore || desc.ignore || ignore_because_panic_abort {
monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
return;
}
@ -1445,7 +1462,9 @@ pub fn run_test(opts: &TestOpts,
// If the platform is single-threaded we're just going to run
// the test synchronously, regardless of the concurrency
// level.
let supports_threads = !cfg!(target_os = "emscripten");
let supports_threads =
!cfg!(target_os = "emscripten") &&
!cfg!(target_arch = "wasm32");
if supports_threads {
let cfg = thread::Builder::new().name(match name {
DynTestName(ref name) => name.clone(),

View File

@ -20,13 +20,20 @@
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
#[cfg(not(target_env = "msvc"))]
extern crate libc;
#[macro_use]
mod macros;
#[cfg(not(target_env = "msvc"))]
mod libunwind;
#[cfg(not(target_env = "msvc"))]
pub use libunwind::*;
cfg_if! {
if #[cfg(target_env = "msvc")] {
// no extra unwinder support needed
} else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
// no unwinder on the system!
} else {
extern crate libc;
mod libunwind;
pub use libunwind::*;
}
}
#[cfg(all(target_env = "musl", not(target_arch = "mips")))]
#[link(name = "unwind", kind = "static", cfg(target_feature = "crt-static"))]

45
src/libunwind/macros.rs Normal file
View File

@ -0,0 +1,45 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// A macro for defining #[cfg] if-else statements.
///
/// This is similar to the `if/elif` C preprocessor macro by allowing definition
/// of a cascade of `#[cfg]` cases, emitting the implementation which matches
/// first.
///
/// This allows you to conveniently provide a long list #[cfg]'d blocks of code
/// without having to rewrite each clause multiple times.
macro_rules! cfg_if {
($(
if #[cfg($($meta:meta),*)] { $($it:item)* }
) else * else {
$($it2:item)*
}) => {
__cfg_if_items! {
() ;
$( ( ($($meta),*) ($($it)*) ), )*
( () ($($it2)*) ),
}
}
}
macro_rules! __cfg_if_items {
(($($not:meta,)*) ; ) => {};
(($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => {
__cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* }
__cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* }
}
}
macro_rules! __cfg_if_apply {
($m:meta, $($it:item)*) => {
$(#[$m] $it)*
}
}

View File

@ -0,0 +1,14 @@
[package]
name = "dlmalloc"
version = "0.0.0"
authors = ["The Rust Project Developers"]
[lib]
path = "../../dlmalloc/src/lib.rs"
test = false
bench = false
doc = false
[dependencies]
core = { path = "../../libcore" }
alloc = { path = "../../liballoc" }

View File

@ -366,7 +366,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
LLVMRustCodeModel RustCM, LLVMRustRelocMode RustReloc,
LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
bool PositionIndependentExecutable, bool FunctionSections,
bool DataSections, bool TrapUnreachable) {
bool DataSections,
bool TrapUnreachable,
bool Singlethread) {
auto CM = fromRust(RustCM);
auto OptLevel = fromRust(RustOptLevel);
@ -406,6 +408,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
Options.TrapUnreachable = true;
}
if (Singlethread) {
Options.ThreadModel = ThreadModel::Single;
}
TargetMachine *TM = TheTarget->createTargetMachine(
Trip.getTriple(), RealCPU, Feature, Options, RM, CM, OptLevel);
return wrap(TM);

View File

@ -0,0 +1,14 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![wasm_import_memory] //~ ERROR: currently unstable
fn main() {}

View File

@ -0,0 +1,163 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:--test
#![feature(rustc_private, flt2dec)]
extern crate core;
extern crate rand;
use std::i16;
use std::mem;
use std::str;
use core::num::flt2dec::MAX_SIG_DIGITS;
use core::num::flt2dec::strategy::grisu::format_exact_opt;
use core::num::flt2dec::strategy::grisu::format_shortest_opt;
use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded};
use rand::{Rand, XorShiftRng};
use rand::distributions::{IndependentSample, Range};
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
match decode(v).1 {
FullDecoded::Finite(decoded) => decoded,
full_decoded => panic!("expected finite, got {:?} instead", full_decoded)
}
}
fn iterate<F, G, V>(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16),
V: FnMut(usize) -> Decoded {
assert!(k <= 1024);
let mut npassed = 0; // f(x) = Some(g(x))
let mut nignored = 0; // f(x) = None
for i in 0..n {
if (i & 0xfffff) == 0 {
println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})",
i, n, nignored, npassed, i - nignored - npassed);
}
let decoded = v(i);
let mut buf1 = [0; 1024];
if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) {
let mut buf2 = [0; 1024];
let (len2, e2) = g(&decoded, &mut buf2[..k]);
if e1 == e2 && &buf1[..len1] == &buf2[..len2] {
npassed += 1;
} else {
println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}",
i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1,
str::from_utf8(&buf2[..len2]).unwrap(), e2);
}
} else {
nignored += 1;
}
}
println!("{}({}): done, ignored={} passed={} failed={}",
func, k, nignored, npassed, n - nignored - npassed);
assert!(nignored + npassed == n,
"{}({}): {} out of {} values returns an incorrect value!",
func, k, n - nignored - npassed, n);
(npassed, nignored)
}
pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000);
iterate("f32_random_equivalence_test", k, n, f, g, |_| {
let i: u32 = f32_range.ind_sample(&mut rng);
let x: f32 = unsafe {mem::transmute(i)};
decode_finite(x)
});
}
pub fn f64_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng());
let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
iterate("f64_random_equivalence_test", k, n, f, g, |_| {
let i: u64 = f64_range.ind_sample(&mut rng);
let x: f64 = unsafe {mem::transmute(i)};
decode_finite(x)
});
}
pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>,
G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) {
// we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values,
// so why not simply testing all of them?
//
// this is of course very stressful (and thus should be behind an `#[ignore]` attribute),
// but with `-C opt-level=3 -C lto` this only takes about an hour or so.
// iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges
let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test",
k, 0x7f7f_ffff, f, g, |i: usize| {
let x: f32 = unsafe {mem::transmute(i as u32 + 1)};
decode_finite(x)
});
assert_eq!((npassed, nignored), (2121451881, 17643158));
}
#[test]
fn shortest_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000);
}
#[test] #[ignore] // it is too expensive
fn shortest_f32_exhaustive_equivalence_test() {
// it is hard to directly test the optimality of the output, but we can at least test if
// two different algorithms agree to each other.
//
// this reports the progress and the number of f32 values returned `None`.
// with `--nocapture` (and plenty of time and appropriate rustc flags), this should print:
// `done, ignored=17643158 passed=2121451881 failed=0`.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}
#[test] #[ignore] // it is too expensive
fn shortest_f64_hard_random_equivalence_test() {
// this again probably has to use appropriate rustc flags.
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f64_random_equivalence_test(format_shortest_opt, fallback,
MAX_SIG_DIGITS, 100_000_000);
}
#[test]
fn exact_f32_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}
#[test]
fn exact_f64_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
for k in 1..21 {
f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN), k, 1_000);
}
}

View File

@ -0,0 +1,83 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_private, sort_internals)]
extern crate core;
extern crate rand;
use std::cmp::Ordering::{Equal, Greater, Less};
use core::slice::heapsort;
use rand::{Rng, XorShiftRng};
fn main() {
let mut v = [0; 600];
let mut tmp = [0; 600];
let mut rng = XorShiftRng::new_unseeded();
for len in (2..25).chain(500..510) {
let v = &mut v[0..len];
let tmp = &mut tmp[0..len];
for &modulus in &[5, 10, 100, 1000] {
for _ in 0..100 {
for i in 0..len {
v[i] = rng.gen::<i32>() % modulus;
}
// Sort in default order.
tmp.copy_from_slice(v);
tmp.sort_unstable();
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Sort in ascending order.
tmp.copy_from_slice(v);
tmp.sort_unstable_by(|a, b| a.cmp(b));
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Sort in descending order.
tmp.copy_from_slice(v);
tmp.sort_unstable_by(|a, b| b.cmp(a));
assert!(tmp.windows(2).all(|w| w[0] >= w[1]));
// Test heapsort using `<` operator.
tmp.copy_from_slice(v);
heapsort(tmp, |a, b| a < b);
assert!(tmp.windows(2).all(|w| w[0] <= w[1]));
// Test heapsort using `>` operator.
tmp.copy_from_slice(v);
heapsort(tmp, |a, b| a > b);
assert!(tmp.windows(2).all(|w| w[0] >= w[1]));
}
}
}
// Sort using a completely random comparison function.
// This will reorder the elements *somehow*, but won't panic.
for i in 0..v.len() {
v[i] = i as i32;
}
v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap());
v.sort_unstable();
for i in 0..v.len() {
assert_eq!(v[i], i as i32);
}
// Should not panic.
[0i32; 0].sort_unstable();
[(); 10].sort_unstable();
[(); 100].sort_unstable();
let mut v = [0xDEADBEEFu64];
v.sort_unstable();
assert!(v == [0xDEADBEEF]);
}

View File

@ -15,7 +15,6 @@
extern crate helper;
use std::env;
use std::heap::{Heap, Alloc, System, Layout, AllocErr};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
@ -39,8 +38,7 @@ unsafe impl<'a> Alloc for &'a A {
static GLOBAL: A = A;
fn main() {
env::set_var("FOO", "bar");
drop(env::var("FOO"));
println!("hello!");
let n = HITS.load(Ordering::SeqCst);
assert!(n > 0);

View File

@ -17,7 +17,6 @@
extern crate custom;
extern crate helper;
use std::env;
use std::heap::{Heap, Alloc, System, Layout};
use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT};

View File

@ -19,7 +19,6 @@ extern crate custom;
extern crate custom_as_global;
extern crate helper;
use std::env;
use std::heap::{Heap, Alloc, System, Layout};
use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT};

View File

@ -10,6 +10,7 @@
// aux-build:anon-extern-mod-cross-crate-1.rs
// pretty-expanded FIXME #23616
// ignore-wasm32-bare no libc to test ffi with
extern crate anonexternmod;

View File

@ -9,6 +9,7 @@
// except according to those terms.
// pretty-expanded FIXME #23616
// ignore-wasm32-bare no libc to test ffi with
#![feature(libc)]

View File

@ -10,13 +10,10 @@
#![crate_name="socketlib"]
#![crate_type = "lib"]
#![feature(libc)]
pub mod socket {
extern crate libc;
pub struct socket_handle {
sockfd: libc::c_int,
sockfd: u32,
}
impl Drop for socket_handle {
@ -25,7 +22,7 @@ pub mod socket {
}
}
pub fn socket_handle(x: libc::c_int) -> socket_handle {
pub fn socket_handle(x: u32) -> socket_handle {
socket_handle {
sockfd: x
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare compiled with panic=abort by default
// Test that builtin implementations of `Clone` cleanup everything
// in case of unwinding.

View File

@ -9,6 +9,7 @@
// except according to those terms.
// pretty-expanded FIXME #23616
// ignore-wasm32-bare no libc to test ffi with
#![feature(libc)]

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc to test with
#![feature(libc, std_misc)]

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc to test ffi with
#[link(name = "rust_test_helpers", kind = "static")]
extern {
fn rust_int8_to_int32(_: i8) -> i32;

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare compiled with panic=abort by default
fn worker() -> ! {
panic!()
}

View File

@ -9,6 +9,7 @@
// except according to those terms.
// pretty-expanded FIXME #23616
// ignore-wasm32-bare no target_family
#[cfg(windows)]
pub fn main() {

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no target_family
// pretty-expanded FIXME #23616
#[cfg(target_family = "windows")]

View File

@ -11,6 +11,7 @@
// Static recursion check shouldn't fail when given a foreign item (#18279)
// aux-build:check_static_recursion_foreign_helper.rs
// ignore-wasm32-bare no libc to test ffi with
// pretty-expanded FIXME #23616

View File

@ -8,21 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(libc)]
extern crate libc;
struct TestStruct {
x: *const libc::c_void
x: *const u8,
}
unsafe impl Sync for TestStruct {}
extern fn foo() {}
const x: extern "C" fn() = foo;
static y: TestStruct = TestStruct { x: x as *const libc::c_void };
static y: TestStruct = TestStruct { x: x as *const u8 };
pub fn main() {
assert_eq!(x as *const libc::c_void, y.x);
assert_eq!(x as *const u8, y.x);
}

View File

@ -11,6 +11,7 @@
// aux-build:anon-extern-mod-cross-crate-1.rs
// aux-build:anon-extern-mod-cross-crate-1.rs
// pretty-expanded FIXME #23616
// ignore-wasm32-bare no libc to test ffi with
extern crate anonexternmod;

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare compiled with panic=abort by default
#![feature(generators, generator_trait, untagged_unions)]
use std::cell::{Cell, RefCell};

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no env vars
use std::env::*;

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc to test ffi with
#![feature(libc)]
extern crate libc;

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc to test ffi with
#![feature(libc)]
extern crate libc;

View File

@ -8,7 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//aux-build:extern-crosscrate-source.rs
// aux-build:extern-crosscrate-source.rs
// ignore-wasm32-bare no libc to test ffi with
#![feature(libc)]

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a foreign function that accepts and returns a struct
// by value.

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a foreign function that accepts and returns a struct
// by value.

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a foreign function that accepts and returns a struct
// by value.

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a foreign function that accepts and returns a struct
// by value.

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a function that takes/returns a u8.

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
#[link(name = "rust_test_helpers", kind = "static")]
extern {

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a function that takes/returns a u32.

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc for ffi testing
// Test a call to a function that takes/returns a u64.

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-wasm32-bare no libc to test ffi with
pub struct TwoU16s {
one: u16, two: u16

Some files were not shown because too many files have changed in this diff Show More