]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/back/wasm.rs
Simplify SaveHandler trait
[rust.git] / src / librustc_codegen_ssa / back / wasm.rs
1 use std::fs;
2 use std::path::Path;
3 use std::str;
4
5 use rustc_serialize::leb128;
6
7 // https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec
8 const WASM_CUSTOM_SECTION_ID: u8 = 0;
9
10 /// Adds or augment the existing `producers` section to encode information about
11 /// the Rust compiler used to produce the wasm file.
12 pub fn add_producer_section(
13     path: &Path,
14     rust_version: &str,
15     rustc_version: &str,
16 ) {
17     struct Field<'a> {
18         name: &'a str,
19         values: Vec<FieldValue<'a>>,
20     }
21
22     #[derive(Copy, Clone)]
23     struct FieldValue<'a> {
24         name: &'a str,
25         version: &'a str,
26     }
27
28     let wasm = fs::read(path).expect("failed to read wasm output");
29     let mut ret = WasmEncoder::new();
30     ret.data.extend(&wasm[..8]);
31
32     // skip the 8 byte wasm/version header
33     let rustc_value = FieldValue {
34         name: "rustc",
35         version: rustc_version,
36     };
37     let rust_value = FieldValue {
38         name: "Rust",
39         version: rust_version,
40     };
41     let mut fields = Vec::new();
42     let mut wrote_rustc = false;
43     let mut wrote_rust = false;
44
45     // Move all sections from the original wasm file to our output, skipping
46     // everything except the producers section
47     for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) {
48         if id != WASM_CUSTOM_SECTION_ID {
49             ret.byte(id);
50             ret.bytes(raw);
51             continue
52         }
53         let mut decoder = WasmDecoder::new(raw);
54         if decoder.str() != "producers" {
55             ret.byte(id);
56             ret.bytes(raw);
57             continue
58         }
59
60         // Read off the producers section into our fields outside the loop,
61         // we'll re-encode the producers section when we're done (to handle an
62         // entirely missing producers section as well).
63         info!("rewriting existing producers section");
64
65         for _ in 0..decoder.u32() {
66             let name = decoder.str();
67             let mut values = Vec::new();
68             for _ in 0..decoder.u32() {
69                 let name = decoder.str();
70                 let version = decoder.str();
71                 values.push(FieldValue { name, version });
72             }
73
74             if name == "language" {
75                 values.push(rust_value);
76                 wrote_rust = true;
77             } else if name == "processed-by" {
78                 values.push(rustc_value);
79                 wrote_rustc = true;
80             }
81             fields.push(Field { name, values });
82         }
83     }
84
85     if !wrote_rust {
86         fields.push(Field {
87             name: "language",
88             values: vec![rust_value],
89         });
90     }
91     if !wrote_rustc {
92         fields.push(Field {
93             name: "processed-by",
94             values: vec![rustc_value],
95         });
96     }
97
98     // Append the producers section to the end of the wasm file.
99     let mut section = WasmEncoder::new();
100     section.str("producers");
101     section.u32(fields.len() as u32);
102     for field in fields {
103         section.str(field.name);
104         section.u32(field.values.len() as u32);
105         for value in field.values {
106             section.str(value.name);
107             section.str(value.version);
108         }
109     }
110     ret.byte(WASM_CUSTOM_SECTION_ID);
111     ret.bytes(&section.data);
112
113     fs::write(path, &ret.data).expect("failed to write wasm output");
114 }
115
116 struct WasmSections<'a>(WasmDecoder<'a>);
117
118 impl<'a> Iterator for WasmSections<'a> {
119     type Item = (u8, &'a [u8]);
120
121     fn next(&mut self) -> Option<(u8, &'a [u8])> {
122         if self.0.data.is_empty() {
123             return None
124         }
125
126         // see https://webassembly.github.io/spec/core/binary/modules.html#sections
127         let id = self.0.byte();
128         let section_len = self.0.u32();
129         info!("new section {} / {} bytes", id, section_len);
130         let section = self.0.skip(section_len as usize);
131         Some((id, section))
132     }
133 }
134
135 struct WasmDecoder<'a> {
136     data: &'a [u8],
137 }
138
139 impl<'a> WasmDecoder<'a> {
140     fn new(data: &'a [u8]) -> WasmDecoder<'a> {
141         WasmDecoder { data }
142     }
143
144     fn byte(&mut self) -> u8 {
145         self.skip(1)[0]
146     }
147
148     fn u32(&mut self) -> u32 {
149         let (n, l1) = leb128::read_u32_leb128(self.data);
150         self.data = &self.data[l1..];
151         return n
152     }
153
154     fn skip(&mut self, amt: usize) -> &'a [u8] {
155         let (data, rest) = self.data.split_at(amt);
156         self.data = rest;
157         data
158     }
159
160     fn str(&mut self) -> &'a str {
161         let len = self.u32();
162         str::from_utf8(self.skip(len as usize)).unwrap()
163     }
164 }
165
166 struct WasmEncoder {
167     data: Vec<u8>,
168 }
169
170 impl WasmEncoder {
171     fn new() -> WasmEncoder {
172         WasmEncoder { data: Vec::new() }
173     }
174
175     fn u32(&mut self, val: u32) {
176         leb128::write_u32_leb128(&mut self.data, val);
177     }
178
179     fn byte(&mut self, val: u8) {
180         self.data.push(val);
181     }
182
183     fn bytes(&mut self, val: &[u8]) {
184         self.u32(val.len() as u32);
185         self.data.extend_from_slice(val);
186     }
187
188     fn str(&mut self, val: &str) {
189         self.bytes(val.as_bytes())
190     }
191 }