1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
extern crate binjs;
extern crate clap;
extern crate env_logger;
use binjs::source::Shift;
use binjs::specialized::es6::io::Decoder;
use std::fs::{self, File};
use std::io::*;
use std::thread;
use clap::*;
macro_rules! progress {
($quiet:expr, $($args:tt)*) => {
if !$quiet {
println!($($args)*);
}
}
}
struct Options<'a> {
print_json: bool,
dest_path: Option<&'a str>,
format: binjs::io::Format,
}
fn main() {
thread::Builder::new()
.name("large stack dedicated thread".to_string())
.stack_size(20 * 1024 * 1024)
.spawn(|| {
main_aux();
})
.expect("Could not launch dedicated thread")
.join()
.expect("Error in dedicated thread");
}
fn main_aux() {
env_logger::init();
let matches = App::new("BinJS decoder")
.author("David Teller, <dteller@mozilla.com>")
.about("Decode a JavaScript BinJS source to a JavaScript text source.")
.args(&[
Arg::with_name("INPUT").help(
"Input file to use. Must be a BinJS source file. If not specified, stdin is used",
),
Arg::with_name("OUTPUT")
.help("Output file to use. Will be overwritten. If not specified, stdout is used"),
Arg::with_name("dump")
.long("dump")
.takes_value(false)
.help("If specified, dump a JSON version of the AST."),
Arg::with_name("quiet")
.long("quiet")
.short("q")
.help("Do not print progress"),
Arg::with_name("print-json")
.long("print-json")
.help("Print JSON of parse tree"),
])
.subcommand(binjs::io::Format::subcommand())
.get_matches();
let spec = binjs::generic::es6::Library::spec();
let source_path = matches.value_of("INPUT");
let dest_path = matches.value_of("OUTPUT");
let quiet = matches.is_present("quiet") || dest_path.is_none();
let format =
binjs::io::Format::from_matches(&spec, &matches).expect("Could not parse encoding format");
progress!(quiet, "Using format: {}", format.name());
let mut options = Options {
print_json: matches.is_present("print-json"),
dest_path,
format,
};
progress!(quiet, "Reading.");
let mut tree = match source_path {
Some(path) => parse_tree(
&|| BufReader::new(File::open(path).expect("Could not open source")),
&mut options,
),
None => {
let mut buffer = Vec::new();
stdin()
.read_to_end(&mut buffer)
.expect("Failed to read from stdin");
parse_tree(&|| Cursor::new(&buffer), &mut options)
}
};
if options.print_json {
progress!(quiet, "Printing to screen...");
serde_json::to_writer_pretty(std::io::stdout(), &tree).unwrap();
println!();
}
let cleaner = binjs::specialized::es6::Cleanup {
scoped_dictionaries: true,
..Default::default()
};
cleaner.cleanup(&mut tree);
progress!(quiet, "Pretty-printing");
let printer = Shift::try_new().expect("Could not launch Shift");
let source = printer.to_source(&tree).expect("Could not pretty-print");
progress!(quiet, "Writing.");
match options.dest_path {
Some(path) => {
fs::write(path, source).expect("Could not write destination file");
}
None => {
stdout()
.write(source.as_bytes())
.expect("Could not write destination file");
}
}
}
fn parse_tree<R: Read + Seek>(
get_stream: &dyn Fn() -> R,
options: &mut Options,
) -> binjs::specialized::es6::ast::Script {
let decoder = Decoder::new();
decoder
.decode(&mut options.format, get_stream())
.expect("Could not decode")
}