提交 7df6f416 编写于 作者: A Alex Crichton

rustc: Add a `#[wasm_custom_section]` attribute

This commit is an implementation of adding custom sections to wasm artifacts in
rustc. The intention here is to expose the ability of the wasm binary format to
contain custom sections with arbitrary user-defined data. Currently neither our
version of LLVM nor LLD supports this so the implementation is currently custom
to rustc itself.

The implementation here is to attach a `#[wasm_custom_section = "foo"]`
attribute to any `const` which has a type like `[u8; N]`. Other types of
constants aren't supported yet but may be added one day! This should hopefully
be enough to get off the ground with *some* custom section support.

The current semantics are that any constant tagged with `#[wasm_custom_section]`
section will be *appended* to the corresponding section in the final output wasm
artifact (and this affects dependencies linked in as well, not just the final
crate). This means that whatever is interpreting the contents must be able to
interpret binary-concatenated sections (or each constant needs to be in its own
custom section).

To test this change the existing `run-make` test suite was moved to a
`run-make-fulldeps` folder and a new `run-make` test suite was added which
applies to all targets by default. This test suite currently only has one test
which only runs for the wasm target (using a node.js script to use `WebAssembly`
in JS to parse the wasm output).
上级 5092c6b0
...@@ -313,6 +313,7 @@ fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> { ...@@ -313,6 +313,7 @@ fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
test::RunPassFullDepsPretty, test::RunFailFullDepsPretty, test::RunPassFullDepsPretty, test::RunFailFullDepsPretty,
test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck, test::Crate, test::CrateLibrustc, test::CrateRustdoc, test::Linkcheck,
test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck, test::Cargotest, test::Cargo, test::Rls, test::ErrorIndex, test::Distcheck,
test::RunMakeFullDeps,
test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample, test::Nomicon, test::Reference, test::RustdocBook, test::RustByExample,
test::TheBook, test::UnstableBook, test::TheBook, test::UnstableBook,
test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme, test::Rustfmt, test::Miri, test::Clippy, test::RustdocJS, test::RustdocTheme,
......
...@@ -915,7 +915,7 @@ fn run(self, builder: &Builder) -> Compiler { ...@@ -915,7 +915,7 @@ fn run(self, builder: &Builder) -> Compiler {
} }
} }
let lld_install = if build.config.lld_enabled && target_compiler.stage > 0 { let lld_install = if build.config.lld_enabled {
Some(builder.ensure(native::Lld { Some(builder.ensure(native::Lld {
target: target_compiler.host, target: target_compiler.host,
})) }))
......
...@@ -759,12 +759,18 @@ fn run(self, builder: &Builder) { ...@@ -759,12 +759,18 @@ fn run(self, builder: &Builder) {
host: true host: true
}); });
host_test!(RunMake { default_test!(RunMake {
path: "src/test/run-make", path: "src/test/run-make",
mode: "run-make", mode: "run-make",
suite: "run-make" suite: "run-make"
}); });
host_test!(RunMakeFullDeps {
path: "src/test/run-make-fulldeps",
mode: "run-make",
suite: "run-make-fulldeps"
});
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Compiletest { struct Compiletest {
compiler: Compiler, compiler: Compiler,
...@@ -827,8 +833,7 @@ fn run(self, builder: &Builder) { ...@@ -827,8 +833,7 @@ fn run(self, builder: &Builder) {
// FIXME: Does pretty need librustc compiled? Note that there are // FIXME: Does pretty need librustc compiled? Note that there are
// fulldeps test suites with mode = pretty as well. // fulldeps test suites with mode = pretty as well.
mode == "pretty" || mode == "pretty" ||
mode == "rustdoc" || mode == "rustdoc" {
mode == "run-make" {
builder.ensure(compile::Rustc { compiler, target }); builder.ensure(compile::Rustc { compiler, target });
} }
...@@ -849,7 +854,7 @@ fn run(self, builder: &Builder) { ...@@ -849,7 +854,7 @@ fn run(self, builder: &Builder) {
cmd.arg("--rustc-path").arg(builder.rustc(compiler)); cmd.arg("--rustc-path").arg(builder.rustc(compiler));
// Avoid depending on rustdoc when we don't need it. // Avoid depending on rustdoc when we don't need it.
if mode == "rustdoc" || mode == "run-make" { if mode == "rustdoc" || (mode == "run-make" && suite.ends_with("fulldeps")) {
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host)); cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
} }
...@@ -931,7 +936,7 @@ fn run(self, builder: &Builder) { ...@@ -931,7 +936,7 @@ fn run(self, builder: &Builder) {
// Only pass correct values for these flags for the `run-make` suite as it // Only pass correct values for these flags for the `run-make` suite as it
// requires that a C++ compiler was configured which isn't always the case. // requires that a C++ compiler was configured which isn't always the case.
if suite == "run-make" { if suite == "run-make-fulldeps" {
let llvm_components = output(Command::new(&llvm_config).arg("--components")); let llvm_components = output(Command::new(&llvm_config).arg("--components"));
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags")); let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
cmd.arg("--cc").arg(build.cc(target)) cmd.arg("--cc").arg(build.cc(target))
...@@ -944,12 +949,12 @@ fn run(self, builder: &Builder) { ...@@ -944,12 +949,12 @@ fn run(self, builder: &Builder) {
} }
} }
} }
if suite == "run-make" && !build.config.llvm_enabled { if suite == "run-make-fulldeps" && !build.config.llvm_enabled {
println!("Ignoring run-make test suite as they generally don't work without LLVM"); println!("Ignoring run-make test suite as they generally don't work without LLVM");
return; return;
} }
if suite != "run-make" { if suite != "run-make-fulldeps" {
cmd.arg("--cc").arg("") cmd.arg("--cc").arg("")
.arg("--cxx").arg("") .arg("--cxx").arg("")
.arg("--cflags").arg("") .arg("--cflags").arg("")
......
...@@ -650,6 +650,8 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool { ...@@ -650,6 +650,8 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
[] GetSymbolExportLevel(DefId), [] GetSymbolExportLevel(DefId),
[] WasmCustomSections(CrateNum),
[input] Features, [input] Features,
[] ProgramClausesFor(DefId), [] ProgramClausesFor(DefId),
......
...@@ -25,6 +25,7 @@ enum Target { ...@@ -25,6 +25,7 @@ enum Target {
Struct, Struct,
Union, Union,
Enum, Enum,
Const,
Other, Other,
} }
...@@ -35,6 +36,7 @@ fn from_item(item: &hir::Item) -> Target { ...@@ -35,6 +36,7 @@ fn from_item(item: &hir::Item) -> Target {
hir::ItemStruct(..) => Target::Struct, hir::ItemStruct(..) => Target::Struct,
hir::ItemUnion(..) => Target::Union, hir::ItemUnion(..) => Target::Union,
hir::ItemEnum(..) => Target::Enum, hir::ItemEnum(..) => Target::Enum,
hir::ItemConst(..) => Target::Const,
_ => Target::Other, _ => Target::Other,
} }
} }
...@@ -60,6 +62,17 @@ fn check_attributes(&self, item: &hir::Item, target: Target) { ...@@ -60,6 +62,17 @@ fn check_attributes(&self, item: &hir::Item, target: Target) {
if name == "inline" { if name == "inline" {
self.check_inline(attr, item, target) self.check_inline(attr, item, target)
} }
if name == "wasm_custom_section" {
if target != Target::Const {
self.tcx.sess.span_err(attr.span, "only allowed on consts");
}
if attr.value_str().is_none() {
self.tcx.sess.span_err(attr.span, "must be of the form \
#[wasm_custom_section = \"foo\"]");
}
}
} }
} }
......
...@@ -318,6 +318,11 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt, ...@@ -318,6 +318,11 @@ fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
return true; return true;
} }
// These constants are special for wasm
if attr::contains_name(attrs, "wasm_custom_section") {
return true;
}
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
} }
......
...@@ -678,6 +678,12 @@ fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { ...@@ -678,6 +678,12 @@ fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String {
} }
} }
impl<'tcx> QueryDescription<'tcx> for queries::wasm_custom_sections<'tcx> {
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
format!("custom wasm sections for a crate")
}
}
impl<'tcx> QueryDescription<'tcx> for queries::generics_of<'tcx> { impl<'tcx> QueryDescription<'tcx> for queries::generics_of<'tcx> {
#[inline] #[inline]
fn cache_on_disk(def_id: Self::Key) -> bool { fn cache_on_disk(def_id: Self::Key) -> bool {
......
...@@ -424,6 +424,8 @@ ...@@ -424,6 +424,8 @@
[] fn features_query: features_node(CrateNum) -> Lrc<feature_gate::Features>, [] fn features_query: features_node(CrateNum) -> Lrc<feature_gate::Features>,
[] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>, [] fn program_clauses_for: ProgramClausesFor(DefId) -> Lrc<Vec<Clause<'tcx>>>,
[] fn wasm_custom_sections: WasmCustomSections(CrateNum) -> Lrc<Vec<DefId>>,
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
......
...@@ -940,6 +940,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, ...@@ -940,6 +940,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::Features => { force!(features_query, LOCAL_CRATE); } DepKind::Features => { force!(features_query, LOCAL_CRATE); }
DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); } DepKind::ProgramClausesFor => { force!(program_clauses_for, def_id!()); }
DepKind::WasmCustomSections => { force!(wasm_custom_sections, krate!()); }
} }
true true
......
...@@ -271,6 +271,8 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) } ...@@ -271,6 +271,8 @@ fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) }
Arc::new(cdata.exported_symbols()) Arc::new(cdata.exported_symbols())
} }
wasm_custom_sections => { Lrc::new(cdata.wasm_custom_sections()) }
} }
pub fn provide<'tcx>(providers: &mut Providers<'tcx>) { pub fn provide<'tcx>(providers: &mut Providers<'tcx>) {
......
...@@ -1067,6 +1067,16 @@ pub fn exported_symbols(&self) -> Vec<(ExportedSymbol, SymbolExportLevel)> { ...@@ -1067,6 +1067,16 @@ pub fn exported_symbols(&self) -> Vec<(ExportedSymbol, SymbolExportLevel)> {
.collect() .collect()
} }
pub fn wasm_custom_sections(&self) -> Vec<DefId> {
let sections = self.root
.wasm_custom_sections
.decode(self)
.map(|def_index| self.local_def_id(def_index))
.collect::<Vec<_>>();
info!("loaded wasm sections {:?}", sections);
return sections
}
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) { pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {
let entry = self.entry(id); let entry = self.entry(id);
match entry.kind { match entry.kind {
......
...@@ -435,6 +435,12 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> { ...@@ -435,6 +435,12 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> {
&exported_symbols); &exported_symbols);
let exported_symbols_bytes = self.position() - i; let exported_symbols_bytes = self.position() - i;
// encode wasm custom sections
let wasm_custom_sections = self.tcx.wasm_custom_sections(LOCAL_CRATE);
let wasm_custom_sections = self.tracked(
IsolatedEncoder::encode_wasm_custom_sections,
&wasm_custom_sections);
// Encode and index the items. // Encode and index the items.
i = self.position(); i = self.position();
let items = self.encode_info_for_items(); let items = self.encode_info_for_items();
...@@ -478,6 +484,7 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> { ...@@ -478,6 +484,7 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot> {
def_path_table, def_path_table,
impls, impls,
exported_symbols, exported_symbols,
wasm_custom_sections,
index, index,
}); });
...@@ -1444,6 +1451,11 @@ fn encode_exported_symbols(&mut self, ...@@ -1444,6 +1451,11 @@ fn encode_exported_symbols(&mut self,
.cloned()) .cloned())
} }
fn encode_wasm_custom_sections(&mut self, statics: &[DefId]) -> LazySeq<DefIndex> {
info!("encoding custom wasm section constants {:?}", statics);
self.lazy_seq(statics.iter().map(|id| id.index))
}
fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> { fn encode_dylib_dependency_formats(&mut self, _: ()) -> LazySeq<Option<LinkagePreference>> {
match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) { match self.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) {
Some(arr) => { Some(arr) => {
......
...@@ -204,6 +204,7 @@ pub struct CrateRoot { ...@@ -204,6 +204,7 @@ pub struct CrateRoot {
pub def_path_table: Lazy<hir::map::definitions::DefPathTable>, pub def_path_table: Lazy<hir::map::definitions::DefPathTable>,
pub impls: LazySeq<TraitImpls>, pub impls: LazySeq<TraitImpls>,
pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>, pub exported_symbols: LazySeq<(ExportedSymbol, SymbolExportLevel)>,
pub wasm_custom_sections: LazySeq<DefIndex>,
pub index: LazySeq<index::Index>, pub index: LazySeq<index::Index>,
} }
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use rustc::hir::TransFnAttrFlags; use rustc::hir::{self, TransFnAttrFlags};
use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::session::config::Sanitizer; use rustc::session::config::Sanitizer;
use rustc::ty::TyCtxt;
use rustc::ty::maps::Providers; use rustc::ty::maps::Providers;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
...@@ -161,4 +163,32 @@ pub fn provide(providers: &mut Providers) { ...@@ -161,4 +163,32 @@ pub fn provide(providers: &mut Providers) {
.collect()) .collect())
} }
}; };
providers.wasm_custom_sections = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
let mut finder = WasmSectionFinder { tcx, list: Vec::new() };
tcx.hir.krate().visit_all_item_likes(&mut finder);
Lrc::new(finder.list)
};
}
struct WasmSectionFinder<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
list: Vec<DefId>,
}
impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> {
fn visit_item(&mut self, i: &'tcx hir::Item) {
match i.node {
hir::ItemConst(..) => {}
_ => return,
}
if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) {
self.list.push(self.tcx.hir.local_def_id(i.id));
}
}
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use back::wasm;
use cc::windows_registry; use cc::windows_registry;
use super::archive::{ArchiveBuilder, ArchiveConfig}; use super::archive::{ArchiveBuilder, ArchiveConfig};
use super::bytecode::RLIB_BYTECODE_EXTENSION; use super::bytecode::RLIB_BYTECODE_EXTENSION;
...@@ -810,6 +811,11 @@ fn escape_string(s: &[u8]) -> String { ...@@ -810,6 +811,11 @@ fn escape_string(s: &[u8]) -> String {
Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)),
} }
} }
if sess.opts.target_triple == "wasm32-unknown-unknown" {
wasm::add_custom_sections(&out_filename,
&trans.crate_info.wasm_custom_sections);
}
} }
fn exec_linker(sess: &Session, cmd: &mut Command, tmpdir: &Path) fn exec_linker(sess: &Session, cmd: &mut Command, tmpdir: &Path)
......
// Copyright 2018 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 std::fs;
use std::path::Path;
use std::collections::BTreeMap;
use serialize::leb128;
pub fn add_custom_sections(path: &Path, sections: &BTreeMap<String, Vec<u8>>) {
let mut wasm = fs::read(path).expect("failed to read wasm output");
// see https://webassembly.github.io/spec/core/binary/modules.html#custom-section
for (section, bytes) in sections {
// write the `id` identifier, 0 for a custom section
let len = wasm.len();
leb128::write_u32_leb128(&mut wasm, len, 0);
// figure out how long our name descriptor will be
let mut name = Vec::new();
leb128::write_u32_leb128(&mut name, 0, section.len() as u32);
name.extend_from_slice(section.as_bytes());
// write the length of the payload
let len = wasm.len();
let total_len = bytes.len() + name.len();
leb128::write_u32_leb128(&mut wasm, len, total_len as u32);
// write out the name section
wasm.extend(name);
// and now the payload itself
wasm.extend_from_slice(bytes);
}
fs::write(path, &wasm).expect("failed to write wasm output");
}
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
use CrateInfo; use CrateInfo;
use std::any::Any; use std::any::Any;
use std::collections::BTreeMap;
use std::ffi::CString; use std::ffi::CString;
use std::str; use std::str;
use std::sync::Arc; use std::sync::Arc;
...@@ -1070,8 +1071,24 @@ pub fn new(tcx: TyCtxt) -> CrateInfo { ...@@ -1070,8 +1071,24 @@ pub fn new(tcx: TyCtxt) -> CrateInfo {
used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic),
used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic),
used_crate_source: FxHashMap(), used_crate_source: FxHashMap(),
wasm_custom_sections: BTreeMap::new(),
}; };
let load_wasm_sections = tcx.sess.crate_types.borrow()
.iter()
.any(|c| *c != config::CrateTypeRlib) &&
tcx.sess.opts.target_triple == "wasm32-unknown-unknown";
if load_wasm_sections {
info!("attempting to load all wasm sections");
for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() {
let (name, contents) = fetch_wasm_section(tcx, id);
info.wasm_custom_sections.entry(name)
.or_insert(Vec::new())
.extend(contents);
}
}
for &cnum in tcx.crates().iter() { for &cnum in tcx.crates().iter() {
info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); info.native_libraries.insert(cnum, tcx.native_libraries(cnum));
info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string());
...@@ -1091,6 +1108,14 @@ pub fn new(tcx: TyCtxt) -> CrateInfo { ...@@ -1091,6 +1108,14 @@ pub fn new(tcx: TyCtxt) -> CrateInfo {
if tcx.is_no_builtins(cnum) { if tcx.is_no_builtins(cnum) {
info.is_no_builtins.insert(cnum); info.is_no_builtins.insert(cnum);
} }
if load_wasm_sections {
for &id in tcx.wasm_custom_sections(cnum).iter() {
let (name, contents) = fetch_wasm_section(tcx, id);
info.wasm_custom_sections.entry(name)
.or_insert(Vec::new())
.extend(contents);
}
}
} }
...@@ -1270,3 +1295,44 @@ fn hash_stable<W: StableHasherResult>(&self, ...@@ -1270,3 +1295,44 @@ fn hash_stable<W: StableHasherResult>(&self,
} }
} }
} }
fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec<u8>) {
use rustc::mir::interpret::{GlobalId, Value, PrimVal};
use rustc::middle::const_val::ConstVal;
info!("loading wasm section {:?}", id);
let section = tcx.get_attrs(id)
.iter()
.find(|a| a.check_name("wasm_custom_section"))
.expect("missing #[wasm_custom_section] attribute")
.value_str()
.expect("malformed #[wasm_custom_section] attribute");
let instance = ty::Instance::mono(tcx, id);
let cid = GlobalId {
instance,
promoted: None
};
let param_env = ty::ParamEnv::reveal_all();
let val = tcx.const_eval(param_env.and(cid)).unwrap();
let val = match val.val {
ConstVal::Value(val) => val,
ConstVal::Unevaluated(..) => bug!("should be evaluated"),
};
let val = match val {
Value::ByRef(ptr, _align) => ptr.into_inner_primval(),
ref v => bug!("should be ByRef, was {:?}", v),
};
let mem = match val {
PrimVal::Ptr(mem) => mem,
ref v => bug!("should be Ptr, was {:?}", v),
};
assert_eq!(mem.offset, 0);
let alloc = tcx
.interpret_interner
.get_alloc(mem.alloc_id)
.expect("miri allocation never successfully created");
(section.to_string(), alloc.bytes.clone())
}
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
use std::any::Any; use std::any::Any;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::mpsc; use std::sync::mpsc;
use std::collections::BTreeMap;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc::dep_graph::DepGraph; use rustc::dep_graph::DepGraph;
...@@ -98,6 +99,7 @@ mod back { ...@@ -98,6 +99,7 @@ mod back {
pub mod symbol_export; pub mod symbol_export;
pub mod write; pub mod write;
mod rpath; mod rpath;
mod wasm;
} }
mod abi; mod abi;
...@@ -400,6 +402,7 @@ struct CrateInfo { ...@@ -400,6 +402,7 @@ struct CrateInfo {
used_crate_source: FxHashMap<CrateNum, Lrc<CrateSource>>, used_crate_source: FxHashMap<CrateNum, Lrc<CrateSource>>,
used_crates_static: Vec<(CrateNum, LibSource)>, used_crates_static: Vec<(CrateNum, LibSource)>,
used_crates_dynamic: Vec<(CrateNum, LibSource)>, used_crates_dynamic: Vec<(CrateNum, LibSource)>,
wasm_custom_sections: BTreeMap<String, Vec<u8>>,
} }
__build_diagnostic_array! { librustc_trans, DIAGNOSTICS } __build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
...@@ -1182,9 +1182,15 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item ...@@ -1182,9 +1182,15 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
let _indenter = indenter(); let _indenter = indenter();
match it.node { match it.node {
// Consts can play a role in type-checking, so they are included here. // Consts can play a role in type-checking, so they are included here.
hir::ItemStatic(..) | hir::ItemStatic(..) => {
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
}
hir::ItemConst(..) => { hir::ItemConst(..) => {
tcx.typeck_tables_of(tcx.hir.local_def_id(it.id)); tcx.typeck_tables_of(tcx.hir.local_def_id(it.id));
if it.attrs.iter().any(|a| a.check_name("wasm_custom_section")) {
let def_id = tcx.hir.local_def_id(it.id);
check_const_is_u8_array(tcx, def_id, it.span);
}
} }
hir::ItemEnum(ref enum_definition, _) => { hir::ItemEnum(ref enum_definition, _) => {
check_enum(tcx, check_enum(tcx,
...@@ -1256,6 +1262,21 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item ...@@ -1256,6 +1262,21 @@ pub fn check_item_type<'a,'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Item
} }
} }
fn check_const_is_u8_array<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
span: Span) {
match tcx.type_of(def_id).sty {
ty::TyArray(t, _) => {
match t.sty {
ty::TyUint(ast::UintTy::U8) => return,
_ => {}
}
}
_ => {}
}
tcx.sess.span_err(span, "must be an array of bytes like `[u8; N]`");
}
fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn check_on_unimplemented<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_def_id: DefId, trait_def_id: DefId,
item: &hir::Item) { item: &hir::Item) {
......
...@@ -451,6 +451,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F) ...@@ -451,6 +451,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
// `use path as _;` and `extern crate c as _;` // `use path as _;` and `extern crate c as _;`
(active, underscore_imports, "1.26.0", Some(48216), None), (active, underscore_imports, "1.26.0", Some(48216), None),
// The #[wasm_custom_section] attribute
(active, wasm_custom_section, "1.26.0", None, None),
); );
declare_features! ( declare_features! (
...@@ -1004,6 +1007,11 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool { ...@@ -1004,6 +1007,11 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
"never will be stable", "never will be stable",
cfg_fn!(rustc_attrs))), cfg_fn!(rustc_attrs))),
("wasm_custom_section", Whitelisted, Gated(Stability::Unstable,
"wasm_custom_section",
"attribute is currently unstable",
cfg_fn!(wasm_custom_section))),
// Crate level attributes // Crate level attributes
("crate_name", CrateLevel, Ungated), ("crate_name", CrateLevel, Ungated),
("crate_type", CrateLevel, Ungated), ("crate_type", CrateLevel, Ungated),
......
-include ../../run-make-fulldeps/tools.mk
ifeq ($(TARGET),wasm32-unknown-unknown)
all:
$(RUSTC) foo.rs --target wasm32-unknown-unknown
$(RUSTC) bar.rs -C lto -O --target wasm32-unknown-unknown
$(NODE) foo.js $(TMPDIR)/bar.wasm
else
all:
endif
// Copyright 2018 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.
#![crate_type = "cdylib"]
#![feature(wasm_custom_section)]
#![deny(warnings)]
extern crate foo;
#[wasm_custom_section = "foo"]
const A: [u8; 2] = [5, 6];
#[wasm_custom_section = "baz"]
const B: [u8; 2] = [7, 8];
#[no_mangle]
pub extern fn foo() {}
// Copyright 2018 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.
const fs = require('fs');
const process = require('process');
const assert = require('assert');
const buffer = fs.readFileSync(process.argv[2]);
let m = new WebAssembly.Module(buffer);
let sections = WebAssembly.Module.customSections(m, "baz");
console.log('section baz', sections);
assert.strictEqual(sections.length, 1);
let section = new Uint8Array(sections[0]);
console.log('contents', section);
assert.strictEqual(section.length, 2);
assert.strictEqual(section[0], 7);
assert.strictEqual(section[1], 8);
sections = WebAssembly.Module.customSections(m, "bar");
console.log('section bar', sections);
assert.strictEqual(sections.length, 1, "didn't pick up `bar` section from dependency");
section = new Uint8Array(sections[0]);
console.log('contents', section);
assert.strictEqual(section.length, 2);
assert.strictEqual(section[0], 3);
assert.strictEqual(section[1], 4);
sections = WebAssembly.Module.customSections(m, "foo");
console.log('section foo', sections);
assert.strictEqual(sections.length, 1, "didn't create `foo` section");
section = new Uint8Array(sections[0]);
console.log('contents', section);
assert.strictEqual(section.length, 4, "didn't concatenate `foo` sections");
assert.strictEqual(section[0], 5);
assert.strictEqual(section[1], 6);
assert.strictEqual(section[2], 1);
assert.strictEqual(section[3], 2);
process.exit(1);
// Copyright 2018 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.
#![crate_type = "rlib"]
#![feature(wasm_custom_section)]
#![deny(warnings)]
#[wasm_custom_section = "foo"]
const A: [u8; 2] = [1, 2];
#[wasm_custom_section = "bar"]
const B: [u8; 2] = [3, 4];
// Copyright 2018 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_custom_section = "foo"] //~ ERROR: attribute is currently unstable
const A: [u8; 2] = [1, 2];
fn main() {}
error[E0658]: attribute is currently unstable
--> $DIR/feature-gate-wasm_custom_section.rs:11:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: attribute is currently unstable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(wasm_custom_section)] to the crate attributes to enable
error: aborting due to previous error
If you want more information on this error, try using "rustc --explain E0658"
// Copyright 2018 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(wasm_custom_section)]
#[wasm_custom_section] //~ ERROR: must be of the form
const A: [u8; 1] = [0];
#[wasm_custom_section(foo)] //~ ERROR: must be of the form
const B: [u8; 1] = [0];
fn main() {}
error: must be of the form #[wasm_custom_section = "foo"]
--> $DIR/malformed.rs:13:1
|
LL | #[wasm_custom_section] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^^^^^
error: must be of the form #[wasm_custom_section = "foo"]
--> $DIR/malformed.rs:16:1
|
LL | #[wasm_custom_section(foo)] //~ ERROR: must be of the form
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
// Copyright 2018 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(wasm_custom_section)]
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
static A: [u8; 2] = [1, 2];
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
struct B {}
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
enum C {}
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
impl B {}
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
mod d {}
#[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
fn main() {}
error: only allowed on consts
--> $DIR/not-const.rs:13:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: only allowed on consts
--> $DIR/not-const.rs:16:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: only allowed on consts
--> $DIR/not-const.rs:19:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: only allowed on consts
--> $DIR/not-const.rs:22:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: only allowed on consts
--> $DIR/not-const.rs:25:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: only allowed on consts
--> $DIR/not-const.rs:28:1
|
LL | #[wasm_custom_section = "foo"] //~ ERROR: only allowed on consts
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
// Copyright 2018 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(wasm_custom_section)]
#[wasm_custom_section = "foo"]
const A: u8 = 0; //~ ERROR: must be an array of bytes
#[wasm_custom_section = "foo"]
const B: &[u8] = &[0]; //~ ERROR: must be an array of bytes
#[wasm_custom_section = "foo"]
const C: &[u8; 1] = &[0]; //~ ERROR: must be an array of bytes
fn main() {}
error: must be an array of bytes like `[u8; N]`
--> $DIR/not-slice.rs:14:1
|
LL | const A: u8 = 0; //~ ERROR: must be an array of bytes
| ^^^^^^^^^^^^^^^^
error: must be an array of bytes like `[u8; N]`
--> $DIR/not-slice.rs:17:1
|
LL | const B: &[u8] = &[0]; //~ ERROR: must be an array of bytes
| ^^^^^^^^^^^^^^^^^^^^^^
error: must be an array of bytes like `[u8; N]`
--> $DIR/not-slice.rs:20:1
|
LL | const C: &[u8; 1] = &[0]; //~ ERROR: must be an array of bytes
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
...@@ -2396,13 +2396,6 @@ fn run_rmake_test(&self) { ...@@ -2396,13 +2396,6 @@ fn run_rmake_test(&self) {
.env("S", src_root) .env("S", src_root)
.env("RUST_BUILD_STAGE", &self.config.stage_id) .env("RUST_BUILD_STAGE", &self.config.stage_id)
.env("RUSTC", cwd.join(&self.config.rustc_path)) .env("RUSTC", cwd.join(&self.config.rustc_path))
.env(
"RUSTDOC",
cwd.join(&self.config
.rustdoc_path
.as_ref()
.expect("--rustdoc-path passed")),
)
.env("TMPDIR", &tmpdir) .env("TMPDIR", &tmpdir)
.env("LD_LIB_PATH_ENVVAR", dylib_env_var()) .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
.env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
...@@ -2417,6 +2410,14 @@ fn run_rmake_test(&self) { ...@@ -2417,6 +2410,14 @@ fn run_rmake_test(&self) {
.env_remove("MFLAGS") .env_remove("MFLAGS")
.env_remove("CARGO_MAKEFLAGS"); .env_remove("CARGO_MAKEFLAGS");
if let Some(ref rustdoc) = self.config.rustdoc_path {
cmd.env("RUSTDOC", cwd.join(rustdoc));
}
if let Some(ref node) = self.config.nodejs {
cmd.env("NODE", node);
}
if let Some(ref linker) = self.config.linker { if let Some(ref linker) = self.config.linker {
cmd.env("RUSTC_LINKER", linker); cmd.env("RUSTC_LINKER", linker);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册