mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-13 14:30:02 -04:00
cargo fmt
This commit is contained in:
parent
3d1b5b4c09
commit
9ad3faa331
4 changed files with 478 additions and 472 deletions
2
rewriter/rustfmt.toml
Normal file
2
rewriter/rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
hard_tabs = true
|
|
@ -9,52 +9,52 @@ use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
fn log(s: &str);
|
fn log(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_encode_function(encode: Function) -> EncodeFn {
|
fn create_encode_function(encode: Function) -> EncodeFn {
|
||||||
Box::new(move |str| {
|
Box::new(move |str| {
|
||||||
encode
|
encode
|
||||||
.call1(&JsValue::NULL, &str.into())
|
.call1(&JsValue::NULL, &str.into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_string()
|
.as_string()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string()
|
.to_string()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_str(config: &Object, k: &str) -> String {
|
fn get_str(config: &Object, k: &str) -> String {
|
||||||
Reflect::get(config, &k.into())
|
Reflect::get(config, &k.into())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_string()
|
.as_string()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config(config: Object) -> Config {
|
fn get_config(config: Object) -> Config {
|
||||||
Config {
|
Config {
|
||||||
prefix: get_str(&config, "prefix"),
|
prefix: get_str(&config, "prefix"),
|
||||||
encode: create_encode_function(Reflect::get(&config, &"encode".into()).unwrap().into()),
|
encode: create_encode_function(Reflect::get(&config, &"encode".into()).unwrap().into()),
|
||||||
wrapfn: get_str(&config, "wrapfn"),
|
wrapfn: get_str(&config, "wrapfn"),
|
||||||
importfn: get_str(&config, "importfn"),
|
importfn: get_str(&config, "importfn"),
|
||||||
rewritefn: get_str(&config, "rewritefn"),
|
rewritefn: get_str(&config, "rewritefn"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn rewrite_js(js: &str, url: &str, config: Object) -> Vec<u8> {
|
pub fn rewrite_js(js: &str, url: &str, config: Object) -> Vec<u8> {
|
||||||
rewrite(js, Url::from_str(url).unwrap(), get_config(config))
|
rewrite(js, Url::from_str(url).unwrap(), get_config(config))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, config: Object) -> Vec<u8> {
|
pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, config: Object) -> Vec<u8> {
|
||||||
// we know that this is a valid utf-8 string
|
// we know that this is a valid utf-8 string
|
||||||
let js = unsafe { std::str::from_utf8_unchecked(js) };
|
let js = unsafe { std::str::from_utf8_unchecked(js) };
|
||||||
|
|
||||||
rewrite(js, Url::from_str(url).unwrap(), get_config(config))
|
rewrite(js, Url::from_str(url).unwrap(), get_config(config))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#![allow(clippy::print_stdout)]
|
#![allow(clippy::print_stdout)]
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
env,
|
env,
|
||||||
path::Path,
|
path::Path,
|
||||||
str::{from_utf8, FromStr},
|
str::{from_utf8, FromStr},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod rewrite;
|
pub mod rewrite;
|
||||||
|
@ -24,108 +24,112 @@ use crate::rewrite::Config;
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn encode(data: &str) -> Cow<'_, str> {
|
pub fn encode(data: &str) -> Cow<'_, str> {
|
||||||
encode_binary(data.as_bytes())
|
encode_binary(data.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Percent-encodes every byte except alphanumerics and `-`, `_`, `.`, `~`.
|
/// Percent-encodes every byte except alphanumerics and `-`, `_`, `.`, `~`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn encode_binary(data: &[u8]) -> Cow<'_, str> {
|
pub fn encode_binary(data: &[u8]) -> Cow<'_, str> {
|
||||||
// add maybe extra capacity, but try not to exceed allocator's bucket size
|
// add maybe extra capacity, but try not to exceed allocator's bucket size
|
||||||
let mut escaped = String::new();
|
let mut escaped = String::new();
|
||||||
let _ = escaped.try_reserve(data.len() | 15);
|
let _ = escaped.try_reserve(data.len() | 15);
|
||||||
let unmodified = append_string(data, &mut escaped, true);
|
let unmodified = append_string(data, &mut escaped, true);
|
||||||
if unmodified {
|
if unmodified {
|
||||||
return Cow::Borrowed(unsafe {
|
return Cow::Borrowed(unsafe {
|
||||||
// encode_into has checked it's ASCII
|
// encode_into has checked it's ASCII
|
||||||
std::str::from_utf8_unchecked(data)
|
std::str::from_utf8_unchecked(data)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Cow::Owned(escaped)
|
Cow::Owned(escaped)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_string(data: &[u8], escaped: &mut String, may_skip: bool) -> bool {
|
fn append_string(data: &[u8], escaped: &mut String, may_skip: bool) -> bool {
|
||||||
encode_into(data, may_skip, |s| {
|
encode_into(data, may_skip, |s| {
|
||||||
escaped.push_str(s);
|
escaped.push_str(s);
|
||||||
Ok::<_, std::convert::Infallible>(())
|
Ok::<_, std::convert::Infallible>(())
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_into<E>(
|
fn encode_into<E>(
|
||||||
mut data: &[u8],
|
mut data: &[u8],
|
||||||
may_skip_write: bool,
|
may_skip_write: bool,
|
||||||
mut push_str: impl FnMut(&str) -> Result<(), E>,
|
mut push_str: impl FnMut(&str) -> Result<(), E>,
|
||||||
) -> Result<bool, E> {
|
) -> Result<bool, E> {
|
||||||
let mut pushed = false;
|
let mut pushed = false;
|
||||||
loop {
|
loop {
|
||||||
// Fast path to skip over safe chars at the beginning of the remaining string
|
// Fast path to skip over safe chars at the beginning of the remaining string
|
||||||
let ascii_len = data.iter()
|
let ascii_len = data
|
||||||
.take_while(|&&c| matches!(c, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'.' | b'_' | b'~')).count();
|
.iter()
|
||||||
|
.take_while(
|
||||||
|
|&&c| matches!(c, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'.' | b'_' | b'~'),
|
||||||
|
)
|
||||||
|
.count();
|
||||||
|
|
||||||
let (safe, rest) = if ascii_len >= data.len() {
|
let (safe, rest) = if ascii_len >= data.len() {
|
||||||
if !pushed && may_skip_write {
|
if !pushed && may_skip_write {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
(data, &[][..]) // redundatnt to optimize out a panic in split_at
|
(data, &[][..]) // redundatnt to optimize out a panic in split_at
|
||||||
} else {
|
} else {
|
||||||
data.split_at(ascii_len)
|
data.split_at(ascii_len)
|
||||||
};
|
};
|
||||||
pushed = true;
|
pushed = true;
|
||||||
if !safe.is_empty() {
|
if !safe.is_empty() {
|
||||||
push_str(unsafe { std::str::from_utf8_unchecked(safe) })?;
|
push_str(unsafe { std::str::from_utf8_unchecked(safe) })?;
|
||||||
}
|
}
|
||||||
if rest.is_empty() {
|
if rest.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
match rest.split_first() {
|
match rest.split_first() {
|
||||||
Some((byte, rest)) => {
|
Some((byte, rest)) => {
|
||||||
let enc = &[b'%', to_hex_digit(byte >> 4), to_hex_digit(byte & 15)];
|
let enc = &[b'%', to_hex_digit(byte >> 4), to_hex_digit(byte & 15)];
|
||||||
push_str(unsafe { std::str::from_utf8_unchecked(enc) })?;
|
push_str(unsafe { std::str::from_utf8_unchecked(enc) })?;
|
||||||
data = rest;
|
data = rest;
|
||||||
}
|
}
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_hex_digit(digit: u8) -> u8 {
|
fn to_hex_digit(digit: u8) -> u8 {
|
||||||
match digit {
|
match digit {
|
||||||
0..=9 => b'0' + digit,
|
0..=9 => b'0' + digit,
|
||||||
10..=255 => b'A' - 10 + digit,
|
10..=255 => b'A' - 10 + digit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_string(s: String) -> String {
|
fn encode_string(s: String) -> String {
|
||||||
encode(&s).to_string()
|
encode(&s).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
|
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
|
||||||
let path = Path::new(&name);
|
let path = Path::new(&name);
|
||||||
let source_text = std::fs::read_to_string(path)?;
|
let source_text = std::fs::read_to_string(path)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
from_utf8(
|
from_utf8(
|
||||||
rewrite(
|
rewrite(
|
||||||
&source_text,
|
&source_text,
|
||||||
Url::from_str("https://google.com/glorngle/si.js").unwrap(),
|
Url::from_str("https://google.com/glorngle/si.js").unwrap(),
|
||||||
Config {
|
Config {
|
||||||
prefix: "/scrammedjet/".to_string(),
|
prefix: "/scrammedjet/".to_string(),
|
||||||
encode: Box::new(encode_string),
|
encode: Box::new(encode_string),
|
||||||
wrapfn: "$wrap".to_string(),
|
wrapfn: "$wrap".to_string(),
|
||||||
importfn: "$import".to_string(),
|
importfn: "$import".to_string(),
|
||||||
rewritefn: "$rewrite".to_string(),
|
rewritefn: "$rewrite".to_string(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.as_slice()
|
.as_slice()
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
use oxc_ast::{
|
use oxc_ast::{
|
||||||
ast::{AssignmentTarget, Expression, IdentifierReference, ObjectPropertyKind},
|
ast::{AssignmentTarget, Expression, IdentifierReference, ObjectPropertyKind},
|
||||||
visit::walk,
|
visit::walk,
|
||||||
Visit,
|
Visit,
|
||||||
};
|
};
|
||||||
use oxc_parser::Parser;
|
use oxc_parser::Parser;
|
||||||
use oxc_span::{SourceType, Span};
|
use oxc_span::{SourceType, Span};
|
||||||
|
@ -11,425 +11,425 @@ use url::Url;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum JsChange {
|
enum JsChange {
|
||||||
GenericChange {
|
GenericChange {
|
||||||
span: Span,
|
span: Span,
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
DebugInject {
|
DebugInject {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Assignment {
|
Assignment {
|
||||||
name: String,
|
name: String,
|
||||||
entirespan: Span,
|
entirespan: Span,
|
||||||
rhsspan: Span,
|
rhsspan: Span,
|
||||||
op: AssignmentOperator,
|
op: AssignmentOperator,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EncodeFn = Box<dyn Fn(String) -> String>;
|
pub type EncodeFn = Box<dyn Fn(String) -> String>;
|
||||||
struct Rewriter {
|
struct Rewriter {
|
||||||
jschanges: Vec<JsChange>,
|
jschanges: Vec<JsChange>,
|
||||||
base: Url,
|
base: Url,
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
pub wrapfn: String,
|
pub wrapfn: String,
|
||||||
pub importfn: String,
|
pub importfn: String,
|
||||||
pub rewritefn: String,
|
pub rewritefn: String,
|
||||||
pub encode: EncodeFn,
|
pub encode: EncodeFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rewriter {
|
impl Rewriter {
|
||||||
fn rewrite_url(&mut self, url: String) -> String {
|
fn rewrite_url(&mut self, url: String) -> String {
|
||||||
let url = self.base.join(&url).unwrap();
|
let url = self.base.join(&url).unwrap();
|
||||||
|
|
||||||
let urlencoded = (self.config.encode)(url.to_string());
|
let urlencoded = (self.config.encode)(url.to_string());
|
||||||
|
|
||||||
format!("\"{}{}\"", self.config.prefix, urlencoded)
|
format!("\"{}{}\"", self.config.prefix, urlencoded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Visit<'a> for Rewriter {
|
impl<'a> Visit<'a> for Rewriter {
|
||||||
fn visit_identifier_reference(&mut self, it: &IdentifierReference<'a>) {
|
fn visit_identifier_reference(&mut self, it: &IdentifierReference<'a>) {
|
||||||
// self.jschanges.push(JsChange::GenericChange {
|
// self.jschanges.push(JsChange::GenericChange {
|
||||||
// span: it.span,
|
// span: it.span,
|
||||||
// text: format!(
|
// text: format!(
|
||||||
// "({}(typeof {} == 'undefined' || {}, (()=>{{ try {{return arguments}} catch(_){{}} }})()))",
|
// "({}(typeof {} == 'undefined' || {}, (()=>{{ try {{return arguments}} catch(_){{}} }})()))",
|
||||||
// self.wrapfn, it.name, it.name
|
// self.wrapfn, it.name, it.name
|
||||||
// ),
|
// ),
|
||||||
// });
|
// });
|
||||||
if UNSAFE_GLOBALS.contains(&it.name.to_string().as_str()) {
|
if UNSAFE_GLOBALS.contains(&it.name.to_string().as_str()) {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: it.span,
|
span: it.span,
|
||||||
text: format!("({}({}))", self.config.wrapfn, it.name),
|
text: format!("({}({}))", self.config.wrapfn, it.name),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_this_expression(&mut self, it: &oxc_ast::ast::ThisExpression) {
|
fn visit_this_expression(&mut self, it: &oxc_ast::ast::ThisExpression) {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: it.span,
|
span: it.span,
|
||||||
text: format!("({}(this))", self.config.wrapfn),
|
text: format!("({}(this))", self.config.wrapfn),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_debugger_statement(&mut self, it: &oxc_ast::ast::DebuggerStatement) {
|
fn visit_debugger_statement(&mut self, it: &oxc_ast::ast::DebuggerStatement) {
|
||||||
// delete debugger statements entirely. some sites will spam debugger as an anti-debugging measure, and we don't want that!
|
// delete debugger statements entirely. some sites will spam debugger as an anti-debugging measure, and we don't want that!
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: it.span,
|
span: it.span,
|
||||||
text: "".to_string(),
|
text: "".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can't overwrite window.eval in the normal way because that would make everything an
|
// we can't overwrite window.eval in the normal way because that would make everything an
|
||||||
// indirect eval, which could break things. we handle that edge case here
|
// indirect eval, which could break things. we handle that edge case here
|
||||||
fn visit_call_expression(&mut self, it: &oxc_ast::ast::CallExpression<'a>) {
|
fn visit_call_expression(&mut self, it: &oxc_ast::ast::CallExpression<'a>) {
|
||||||
if let Expression::Identifier(s) = &it.callee {
|
if let Expression::Identifier(s) = &it.callee {
|
||||||
// if it's optional that actually makes it an indirect eval which is handled separately
|
// if it's optional that actually makes it an indirect eval which is handled separately
|
||||||
if s.name == "eval" && !it.optional {
|
if s.name == "eval" && !it.optional {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: Span::new(s.span.start, s.span.end + 1),
|
span: Span::new(s.span.start, s.span.end + 1),
|
||||||
text: format!("eval({}(", self.config.rewritefn),
|
text: format!("eval({}(", self.config.rewritefn),
|
||||||
});
|
});
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: Span::new(it.span.end, it.span.end),
|
span: Span::new(it.span.end, it.span.end),
|
||||||
text: ")".to_string(),
|
text: ")".to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// then we walk the arguments, but not the callee, since we want it to resolve to
|
// then we walk the arguments, but not the callee, since we want it to resolve to
|
||||||
// the real eval
|
// the real eval
|
||||||
walk::walk_arguments(self, &it.arguments);
|
walk::walk_arguments(self, &it.arguments);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
walk::walk_call_expression(self, it);
|
walk::walk_call_expression(self, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_import_declaration(&mut self, it: &oxc_ast::ast::ImportDeclaration<'a>) {
|
fn visit_import_declaration(&mut self, it: &oxc_ast::ast::ImportDeclaration<'a>) {
|
||||||
let name = it.source.value.to_string();
|
let name = it.source.value.to_string();
|
||||||
let text = self.rewrite_url(name);
|
let text = self.rewrite_url(name);
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: it.source.span,
|
span: it.source.span,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
walk::walk_import_declaration(self, it);
|
walk::walk_import_declaration(self, it);
|
||||||
}
|
}
|
||||||
fn visit_import_expression(&mut self, it: &oxc_ast::ast::ImportExpression<'a>) {
|
fn visit_import_expression(&mut self, it: &oxc_ast::ast::ImportExpression<'a>) {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: Span::new(it.span.start, it.span.start + 6),
|
span: Span::new(it.span.start, it.span.start + 6),
|
||||||
text: format!("({}(\"{}\"))", self.config.importfn, self.base),
|
text: format!("({}(\"{}\"))", self.config.importfn, self.base),
|
||||||
});
|
});
|
||||||
walk::walk_import_expression(self, it);
|
walk::walk_import_expression(self, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_export_all_declaration(&mut self, it: &oxc_ast::ast::ExportAllDeclaration<'a>) {
|
fn visit_export_all_declaration(&mut self, it: &oxc_ast::ast::ExportAllDeclaration<'a>) {
|
||||||
let name = it.source.value.to_string();
|
let name = it.source.value.to_string();
|
||||||
let text = self.rewrite_url(name);
|
let text = self.rewrite_url(name);
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: it.source.span,
|
span: it.source.span,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_export_named_declaration(&mut self, it: &oxc_ast::ast::ExportNamedDeclaration<'a>) {
|
fn visit_export_named_declaration(&mut self, it: &oxc_ast::ast::ExportNamedDeclaration<'a>) {
|
||||||
if let Some(source) = &it.source {
|
if let Some(source) = &it.source {
|
||||||
let name = source.value.to_string();
|
let name = source.value.to_string();
|
||||||
let text = self.rewrite_url(name);
|
let text = self.rewrite_url(name);
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: source.span,
|
span: source.span,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// do not walk further, we don't want to rewrite the identifiers
|
// do not walk further, we don't want to rewrite the identifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
fn visit_try_statement(&mut self, it: &oxc_ast::ast::TryStatement<'a>) {
|
fn visit_try_statement(&mut self, it: &oxc_ast::ast::TryStatement<'a>) {
|
||||||
// for debugging we need to know what the error was
|
// for debugging we need to know what the error was
|
||||||
|
|
||||||
if let Some(h) = &it.handler {
|
if let Some(h) = &it.handler {
|
||||||
if let Some(name) = &h.param {
|
if let Some(name) = &h.param {
|
||||||
if let Some(name) = name.pattern.get_identifier() {
|
if let Some(name) = name.pattern.get_identifier() {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: Span::new(h.body.span.start + 1, h.body.span.start + 1),
|
span: Span::new(h.body.span.start + 1, h.body.span.start + 1),
|
||||||
text: format!("$scramerr({});", name),
|
text: format!("$scramerr({});", name),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_object_expression(&mut self, it: &oxc_ast::ast::ObjectExpression<'a>) {
|
fn visit_object_expression(&mut self, it: &oxc_ast::ast::ObjectExpression<'a>) {
|
||||||
for prop in &it.properties {
|
for prop in &it.properties {
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match prop {
|
match prop {
|
||||||
ObjectPropertyKind::ObjectProperty(p) => match &p.value {
|
ObjectPropertyKind::ObjectProperty(p) => match &p.value {
|
||||||
Expression::Identifier(s) => {
|
Expression::Identifier(s) => {
|
||||||
if UNSAFE_GLOBALS.contains(&s.name.to_string().as_str()) && p.shorthand {
|
if UNSAFE_GLOBALS.contains(&s.name.to_string().as_str()) && p.shorthand {
|
||||||
self.jschanges.push(JsChange::GenericChange {
|
self.jschanges.push(JsChange::GenericChange {
|
||||||
span: s.span,
|
span: s.span,
|
||||||
text: format!("{}: ({}({}))", s.name, self.config.wrapfn, s.name),
|
text: format!("{}: ({}({}))", s.name, self.config.wrapfn, s.name),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
walk::walk_object_expression(self, it);
|
walk::walk_object_expression(self, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_return_statement(&mut self, it: &oxc_ast::ast::ReturnStatement<'a>) {
|
fn visit_return_statement(&mut self, it: &oxc_ast::ast::ReturnStatement<'a>) {
|
||||||
self.jschanges.push(JsChange::DebugInject {
|
self.jschanges.push(JsChange::DebugInject {
|
||||||
span: Span::new(it.span.start + 6, it.span.start + 6),
|
span: Span::new(it.span.start + 6, it.span.start + 6),
|
||||||
});
|
});
|
||||||
walk::walk_return_statement(self, it);
|
walk::walk_return_statement(self, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't want to rewrite the identifiers here because of a very specific edge case
|
// we don't want to rewrite the identifiers here because of a very specific edge case
|
||||||
fn visit_for_in_statement(&mut self, it: &oxc_ast::ast::ForInStatement<'a>) {
|
fn visit_for_in_statement(&mut self, it: &oxc_ast::ast::ForInStatement<'a>) {
|
||||||
walk::walk_statement(self, &it.body);
|
walk::walk_statement(self, &it.body);
|
||||||
}
|
}
|
||||||
fn visit_for_of_statement(&mut self, it: &oxc_ast::ast::ForOfStatement<'a>) {
|
fn visit_for_of_statement(&mut self, it: &oxc_ast::ast::ForOfStatement<'a>) {
|
||||||
walk::walk_statement(self, &it.body);
|
walk::walk_statement(self, &it.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_update_expression(&mut self, _it: &oxc_ast::ast::UpdateExpression<'a>) {
|
fn visit_update_expression(&mut self, _it: &oxc_ast::ast::UpdateExpression<'a>) {
|
||||||
// then no, don't walk it, we don't care
|
// then no, don't walk it, we don't care
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_assignment_expression(&mut self, it: &oxc_ast::ast::AssignmentExpression<'a>) {
|
fn visit_assignment_expression(&mut self, it: &oxc_ast::ast::AssignmentExpression<'a>) {
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match &it.left {
|
match &it.left {
|
||||||
AssignmentTarget::AssignmentTargetIdentifier(s) => {
|
AssignmentTarget::AssignmentTargetIdentifier(s) => {
|
||||||
if ["location"].contains(&s.name.to_string().as_str()) {
|
if ["location"].contains(&s.name.to_string().as_str()) {
|
||||||
self.jschanges.push(JsChange::Assignment {
|
self.jschanges.push(JsChange::Assignment {
|
||||||
name: s.name.to_string(),
|
name: s.name.to_string(),
|
||||||
entirespan: it.span,
|
entirespan: it.span,
|
||||||
rhsspan: expression_span(&it.right),
|
rhsspan: expression_span(&it.right),
|
||||||
op: it.operator,
|
op: it.operator,
|
||||||
});
|
});
|
||||||
|
|
||||||
// avoid walking rest of tree, i would need to figure out nested rewrites
|
// avoid walking rest of tree, i would need to figure out nested rewrites
|
||||||
// somehow
|
// somehow
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssignmentTarget::ArrayAssignmentTarget(_) => {
|
AssignmentTarget::ArrayAssignmentTarget(_) => {
|
||||||
// [location] = ["https://example.com"]
|
// [location] = ["https://example.com"]
|
||||||
// this is such a ridiculously specific edge case. just ignore it
|
// this is such a ridiculously specific edge case. just ignore it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// only walk the left side if it isn't an identifier, we can't replace the
|
// only walk the left side if it isn't an identifier, we can't replace the
|
||||||
// identifier with a function obviously
|
// identifier with a function obviously
|
||||||
walk::walk_assignment_target(self, &it.left);
|
walk::walk_assignment_target(self, &it.left);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
walk::walk_expression(self, &it.right);
|
walk::walk_expression(self, &it.right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_span(e: &Expression) -> Span {
|
fn expression_span(e: &Expression) -> Span {
|
||||||
// enums.split("\n").filter(f=>f).map(p=>p.trimLeft()).filter(p=>!p.startsWith("#")).map(p=>p.replace(/\(.*/,"")).map(p=>`E::${p}(s) => s.span`).join(",\n")
|
// enums.split("\n").filter(f=>f).map(p=>p.trimLeft()).filter(p=>!p.startsWith("#")).map(p=>p.replace(/\(.*/,"")).map(p=>`E::${p}(s) => s.span`).join(",\n")
|
||||||
use Expression as E;
|
use Expression as E;
|
||||||
match e {
|
match e {
|
||||||
E::BooleanLiteral(s) => s.span,
|
E::BooleanLiteral(s) => s.span,
|
||||||
E::NullLiteral(s) => s.span,
|
E::NullLiteral(s) => s.span,
|
||||||
E::NumericLiteral(s) => s.span,
|
E::NumericLiteral(s) => s.span,
|
||||||
E::BigIntLiteral(s) => s.span,
|
E::BigIntLiteral(s) => s.span,
|
||||||
E::RegExpLiteral(s) => s.span,
|
E::RegExpLiteral(s) => s.span,
|
||||||
E::StringLiteral(s) => s.span,
|
E::StringLiteral(s) => s.span,
|
||||||
E::TemplateLiteral(s) => s.span,
|
E::TemplateLiteral(s) => s.span,
|
||||||
E::Identifier(s) => s.span,
|
E::Identifier(s) => s.span,
|
||||||
E::MetaProperty(s) => s.span,
|
E::MetaProperty(s) => s.span,
|
||||||
E::Super(s) => s.span,
|
E::Super(s) => s.span,
|
||||||
E::ArrayExpression(s) => s.span,
|
E::ArrayExpression(s) => s.span,
|
||||||
E::ArrowFunctionExpression(s) => s.span,
|
E::ArrowFunctionExpression(s) => s.span,
|
||||||
E::AssignmentExpression(s) => s.span,
|
E::AssignmentExpression(s) => s.span,
|
||||||
E::AwaitExpression(s) => s.span,
|
E::AwaitExpression(s) => s.span,
|
||||||
E::BinaryExpression(s) => s.span,
|
E::BinaryExpression(s) => s.span,
|
||||||
E::CallExpression(s) => s.span,
|
E::CallExpression(s) => s.span,
|
||||||
E::ChainExpression(s) => s.span,
|
E::ChainExpression(s) => s.span,
|
||||||
E::ClassExpression(s) => s.span,
|
E::ClassExpression(s) => s.span,
|
||||||
E::ConditionalExpression(s) => s.span,
|
E::ConditionalExpression(s) => s.span,
|
||||||
E::FunctionExpression(s) => s.span,
|
E::FunctionExpression(s) => s.span,
|
||||||
E::ImportExpression(s) => s.span,
|
E::ImportExpression(s) => s.span,
|
||||||
E::LogicalExpression(s) => s.span,
|
E::LogicalExpression(s) => s.span,
|
||||||
E::NewExpression(s) => s.span,
|
E::NewExpression(s) => s.span,
|
||||||
E::ObjectExpression(s) => s.span,
|
E::ObjectExpression(s) => s.span,
|
||||||
E::ParenthesizedExpression(s) => s.span,
|
E::ParenthesizedExpression(s) => s.span,
|
||||||
E::SequenceExpression(s) => s.span,
|
E::SequenceExpression(s) => s.span,
|
||||||
E::TaggedTemplateExpression(s) => s.span,
|
E::TaggedTemplateExpression(s) => s.span,
|
||||||
E::ThisExpression(s) => s.span,
|
E::ThisExpression(s) => s.span,
|
||||||
E::UnaryExpression(s) => s.span,
|
E::UnaryExpression(s) => s.span,
|
||||||
E::UpdateExpression(s) => s.span,
|
E::UpdateExpression(s) => s.span,
|
||||||
E::YieldExpression(s) => s.span,
|
E::YieldExpression(s) => s.span,
|
||||||
E::PrivateInExpression(s) => s.span,
|
E::PrivateInExpression(s) => s.span,
|
||||||
E::JSXElement(s) => s.span,
|
E::JSXElement(s) => s.span,
|
||||||
E::JSXFragment(s) => s.span,
|
E::JSXFragment(s) => s.span,
|
||||||
E::TSAsExpression(s) => s.span,
|
E::TSAsExpression(s) => s.span,
|
||||||
E::TSSatisfiesExpression(s) => s.span,
|
E::TSSatisfiesExpression(s) => s.span,
|
||||||
E::TSTypeAssertion(s) => s.span,
|
E::TSTypeAssertion(s) => s.span,
|
||||||
E::TSNonNullExpression(s) => s.span,
|
E::TSNonNullExpression(s) => s.span,
|
||||||
E::TSInstantiationExpression(s) => s.span,
|
E::TSInstantiationExpression(s) => s.span,
|
||||||
E::ComputedMemberExpression(s) => s.span,
|
E::ComputedMemberExpression(s) => s.span,
|
||||||
E::StaticMemberExpression(s) => s.span,
|
E::StaticMemberExpression(s) => s.span,
|
||||||
E::PrivateFieldExpression(s) => s.span,
|
E::PrivateFieldExpression(s) => s.span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// js MUST not be able to get a reference to any of these because sbx
|
// js MUST not be able to get a reference to any of these because sbx
|
||||||
const UNSAFE_GLOBALS: [&str; 9] = [
|
const UNSAFE_GLOBALS: [&str; 9] = [
|
||||||
"window",
|
"window",
|
||||||
"self",
|
"self",
|
||||||
"globalThis",
|
"globalThis",
|
||||||
"this",
|
"this",
|
||||||
"parent",
|
"parent",
|
||||||
"top",
|
"top",
|
||||||
"location",
|
"location",
|
||||||
"document",
|
"document",
|
||||||
"eval",
|
"eval",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> {
|
pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> {
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
let source_type = SourceType::default();
|
let source_type = SourceType::default();
|
||||||
let ret = Parser::new(&allocator, js, source_type).parse();
|
let ret = Parser::new(&allocator, js, source_type).parse();
|
||||||
|
|
||||||
for error in ret.errors {
|
for error in ret.errors {
|
||||||
let cloned = js.to_string();
|
let cloned = js.to_string();
|
||||||
let error = error.with_source_code(cloned);
|
let error = error.with_source_code(cloned);
|
||||||
println!("{error:?}");
|
println!("{error:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let program = ret.program;
|
let program = ret.program;
|
||||||
|
|
||||||
// dbg!(&program);
|
// dbg!(&program);
|
||||||
|
|
||||||
let mut ast_pass = Rewriter {
|
let mut ast_pass = Rewriter {
|
||||||
jschanges: Vec::new(),
|
jschanges: Vec::new(),
|
||||||
base: url,
|
base: url,
|
||||||
config,
|
config,
|
||||||
};
|
};
|
||||||
|
|
||||||
ast_pass.visit_program(&program);
|
ast_pass.visit_program(&program);
|
||||||
|
|
||||||
// sorrt changse
|
// sorrt changse
|
||||||
ast_pass.jschanges.sort_by(|a, b| {
|
ast_pass.jschanges.sort_by(|a, b| {
|
||||||
let a = match a {
|
let a = match a {
|
||||||
JsChange::GenericChange { span, text: _ } => span.start,
|
JsChange::GenericChange { span, text: _ } => span.start,
|
||||||
JsChange::Assignment {
|
JsChange::Assignment {
|
||||||
name: _,
|
name: _,
|
||||||
entirespan,
|
entirespan,
|
||||||
rhsspan: _,
|
rhsspan: _,
|
||||||
op: _,
|
op: _,
|
||||||
} => entirespan.start,
|
} => entirespan.start,
|
||||||
JsChange::DebugInject { span } => span.start,
|
JsChange::DebugInject { span } => span.start,
|
||||||
};
|
};
|
||||||
let b = match b {
|
let b = match b {
|
||||||
JsChange::GenericChange { span, text: _ } => span.start,
|
JsChange::GenericChange { span, text: _ } => span.start,
|
||||||
JsChange::Assignment {
|
JsChange::Assignment {
|
||||||
name: _,
|
name: _,
|
||||||
entirespan,
|
entirespan,
|
||||||
rhsspan: _,
|
rhsspan: _,
|
||||||
op: _,
|
op: _,
|
||||||
} => entirespan.start,
|
} => entirespan.start,
|
||||||
JsChange::DebugInject { span } => span.start,
|
JsChange::DebugInject { span } => span.start,
|
||||||
};
|
};
|
||||||
a.cmp(&b)
|
a.cmp(&b)
|
||||||
});
|
});
|
||||||
|
|
||||||
let original_len = js.len();
|
let original_len = js.len();
|
||||||
let mut difference = 0i32;
|
let mut difference = 0i32;
|
||||||
|
|
||||||
for change in &ast_pass.jschanges {
|
for change in &ast_pass.jschanges {
|
||||||
match &change {
|
match &change {
|
||||||
JsChange::GenericChange { span, text } => {
|
JsChange::GenericChange { span, text } => {
|
||||||
difference += text.len() as i32 - (span.end - span.start) as i32;
|
difference += text.len() as i32 - (span.end - span.start) as i32;
|
||||||
}
|
}
|
||||||
JsChange::Assignment {
|
JsChange::Assignment {
|
||||||
name,
|
name,
|
||||||
entirespan,
|
entirespan,
|
||||||
rhsspan: _,
|
rhsspan: _,
|
||||||
op: _,
|
op: _,
|
||||||
} => difference += entirespan.size() as i32 + name.len() as i32 + 10,
|
} => difference += entirespan.size() as i32 + name.len() as i32 + 10,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let size_estimate = (original_len as i32 + difference) as usize;
|
let size_estimate = (original_len as i32 + difference) as usize;
|
||||||
let mut buffer: Vec<u8> = Vec::with_capacity(size_estimate);
|
let mut buffer: Vec<u8> = Vec::with_capacity(size_estimate);
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for change in ast_pass.jschanges {
|
for change in ast_pass.jschanges {
|
||||||
match &change {
|
match &change {
|
||||||
JsChange::GenericChange { span, text } => {
|
JsChange::GenericChange { span, text } => {
|
||||||
let start = span.start as usize;
|
let start = span.start as usize;
|
||||||
let end = span.end as usize;
|
let end = span.end as usize;
|
||||||
|
|
||||||
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes());
|
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes());
|
||||||
|
|
||||||
buffer.extend_from_slice(text.as_bytes());
|
buffer.extend_from_slice(text.as_bytes());
|
||||||
offset = end;
|
offset = end;
|
||||||
}
|
}
|
||||||
JsChange::Assignment {
|
JsChange::Assignment {
|
||||||
name,
|
name,
|
||||||
entirespan,
|
entirespan,
|
||||||
rhsspan,
|
rhsspan,
|
||||||
op,
|
op,
|
||||||
} => {
|
} => {
|
||||||
let start = entirespan.start as usize;
|
let start = entirespan.start as usize;
|
||||||
buffer.extend_from_slice(js[offset..start].as_bytes());
|
buffer.extend_from_slice(js[offset..start].as_bytes());
|
||||||
|
|
||||||
buffer.extend_from_slice(
|
buffer.extend_from_slice(
|
||||||
format!(
|
format!(
|
||||||
"((t)=>$scramjet$tryset({},\"{}\",t)||{}{}t)({})",
|
"((t)=>$scramjet$tryset({},\"{}\",t)||{}{}t)({})",
|
||||||
name,
|
name,
|
||||||
fmt_op(*op),
|
fmt_op(*op),
|
||||||
name,
|
name,
|
||||||
fmt_op(*op),
|
fmt_op(*op),
|
||||||
&js[rhsspan.start as usize..rhsspan.end as usize]
|
&js[rhsspan.start as usize..rhsspan.end as usize]
|
||||||
)
|
)
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
offset = entirespan.end as usize;
|
offset = entirespan.end as usize;
|
||||||
}
|
}
|
||||||
JsChange::DebugInject { span } => {
|
JsChange::DebugInject { span } => {
|
||||||
let start = span.start as usize;
|
let start = span.start as usize;
|
||||||
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes());
|
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes());
|
||||||
|
|
||||||
offset = span.end as usize;
|
offset = span.end as usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer.extend_from_slice(js[offset..].as_bytes());
|
buffer.extend_from_slice(js[offset..].as_bytes());
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_op(op: AssignmentOperator) -> &'static str {
|
fn fmt_op(op: AssignmentOperator) -> &'static str {
|
||||||
match op {
|
match op {
|
||||||
AssignmentOperator::Assign => "=",
|
AssignmentOperator::Assign => "=",
|
||||||
AssignmentOperator::Addition => "+=",
|
AssignmentOperator::Addition => "+=",
|
||||||
AssignmentOperator::Subtraction => "-=",
|
AssignmentOperator::Subtraction => "-=",
|
||||||
AssignmentOperator::Multiplication => "*=",
|
AssignmentOperator::Multiplication => "*=",
|
||||||
AssignmentOperator::Division => "/=",
|
AssignmentOperator::Division => "/=",
|
||||||
AssignmentOperator::Remainder => "%=",
|
AssignmentOperator::Remainder => "%=",
|
||||||
AssignmentOperator::Exponential => "**=",
|
AssignmentOperator::Exponential => "**=",
|
||||||
AssignmentOperator::ShiftLeft => "<<=",
|
AssignmentOperator::ShiftLeft => "<<=",
|
||||||
AssignmentOperator::ShiftRight => ">>=",
|
AssignmentOperator::ShiftRight => ">>=",
|
||||||
AssignmentOperator::ShiftRightZeroFill => ">>>=",
|
AssignmentOperator::ShiftRightZeroFill => ">>>=",
|
||||||
AssignmentOperator::BitwiseAnd => "&=",
|
AssignmentOperator::BitwiseAnd => "&=",
|
||||||
AssignmentOperator::BitwiseXOR => "^=",
|
AssignmentOperator::BitwiseXOR => "^=",
|
||||||
AssignmentOperator::BitwiseOR => "|=",
|
AssignmentOperator::BitwiseOR => "|=",
|
||||||
AssignmentOperator::LogicalAnd => "&&=",
|
AssignmentOperator::LogicalAnd => "&&=",
|
||||||
AssignmentOperator::LogicalOr => "||=",
|
AssignmentOperator::LogicalOr => "||=",
|
||||||
AssignmentOperator::LogicalNullish => "??=",
|
AssignmentOperator::LogicalNullish => "??=",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue