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