jschange -> rewrite

This commit is contained in:
Toshit Chawda 2024-12-12 11:29:55 -08:00
parent 83ccb7602e
commit 6d03da6d85
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
4 changed files with 85 additions and 61 deletions

View file

@ -30,7 +30,7 @@ fn dorewrite(data: &str) -> Result<RewriteResult> {
capture_errors: true, capture_errors: true,
do_sourcemaps: true, do_sourcemaps: true,
scramitize: false, scramitize: true,
strict_rewrites: true, strict_rewrites: true,
}, },
) )

View file

@ -4,10 +4,10 @@ use oxc::{
}; };
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::cfg::Config; use crate::{cfg::Config, RewriterError};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum JsChange { pub enum Rewrite {
/// `(cfg.wrapfn(ident))` | `cfg.wrapfn(ident)` /// `(cfg.wrapfn(ident))` | `cfg.wrapfn(ident)`
WrapFn { WrapFn {
span: Span, span: Span,
@ -72,21 +72,7 @@ pub enum JsChange {
}, },
} }
type Changes<'a> = SmallVec<[&'a str; 8]>; impl Rewrite {
enum JsChangeInner<'a> {
Wrap {
/// Changes to add before span
before: Changes<'a>,
/// Span to add in between
span: &'a Span,
/// Changes to add after span
after: Changes<'a>,
},
Replace(Changes<'a>),
}
impl JsChange {
pub fn get_span(&self) -> &Span { pub fn get_span(&self) -> &Span {
match self { match self {
Self::WrapFn { span, .. } => span, Self::WrapFn { span, .. } => span,
@ -120,25 +106,27 @@ impl JsChange {
if *wrapped { if *wrapped {
JsChangeInner::Wrap { JsChangeInner::Wrap {
before: smallvec!["(", cfg.wrapfn.as_str(), "("], before: smallvec!["(", cfg.wrapfn.as_str(), "("],
span, inner: span,
after: smallvec!["))"], after: smallvec!["))"],
} }
} else { } else {
JsChangeInner::Wrap { JsChangeInner::Wrap {
before: smallvec![cfg.wrapfn.as_str(), "("], before: smallvec![cfg.wrapfn.as_str(), "("],
span, inner: span,
after: smallvec![")"], after: smallvec![")"],
} }
} }
} }
Self::SetRealmFn { span } => JsChangeInner::Wrap { Self::SetRealmFn { span } => JsChangeInner::Wrap {
before: smallvec![cfg.setrealmfn.as_str(), "({})."], before: smallvec![cfg.setrealmfn.as_str(), "({})."],
span, inner: span,
after: smallvec![], after: smallvec![],
}, },
Self::WrapThisFn { .. } => { Self::WrapThisFn { span } => JsChangeInner::Wrap {
JsChangeInner::Replace(smallvec![cfg.wrapthisfn.as_str(), "(this)"]) before: smallvec![cfg.wrapthisfn.as_str(), "("],
} inner: span,
after: smallvec![")"],
},
Self::ImportFn { .. } => JsChangeInner::Replace(smallvec![ Self::ImportFn { .. } => JsChangeInner::Replace(smallvec![
"(", "(",
cfg.importfn.as_str(), cfg.importfn.as_str(),
@ -153,18 +141,19 @@ impl JsChange {
"\")" "\")"
]), ]),
// maps to insert
Self::ScramErr { name, .. } => { Self::ScramErr { name, .. } => {
JsChangeInner::Replace(smallvec!["$scramerr(", name.as_str(), ");"]) JsChangeInner::Replace(smallvec!["$scramerr(", name.as_str(), ");"])
} }
Self::Scramitize { span } => JsChangeInner::Wrap { Self::Scramitize { span } => JsChangeInner::Wrap {
before: smallvec!["$scramitize("], before: smallvec!["$scramitize("],
span, inner: span,
after: smallvec![")"], after: smallvec![")"],
}, },
Self::Eval { inner, .. } => JsChangeInner::Wrap { Self::Eval { inner, .. } => JsChangeInner::Wrap {
before: smallvec!["eval(", cfg.rewritefn.as_str(), "("], before: smallvec!["eval(", cfg.rewritefn.as_str(), "("],
span: inner, inner,
after: smallvec![")"], after: smallvec![")"],
}, },
Self::Assignment { Self::Assignment {
@ -180,9 +169,10 @@ impl JsChange {
op.as_str(), op.as_str(),
"t))(" "t))("
], ],
span: rhsspan, inner: rhsspan,
after: smallvec![")"], after: smallvec![")"],
}, },
// maps to insert
Self::ShorthandObj { name, .. } => JsChangeInner::Replace(smallvec![ Self::ShorthandObj { name, .. } => JsChangeInner::Replace(smallvec![
name.as_str(), name.as_str(),
":", ":",
@ -191,6 +181,7 @@ impl JsChange {
name.as_str(), name.as_str(),
")" ")"
]), ]),
// maps to insert
Self::SourceTag { tagname, .. } => JsChangeInner::Replace(smallvec![ Self::SourceTag { tagname, .. } => JsChangeInner::Replace(smallvec![
"/*scramtag ", "/*scramtag ",
tagname.as_str(), tagname.as_str(),
@ -204,25 +195,41 @@ impl JsChange {
} }
} }
impl PartialOrd for JsChange { impl PartialOrd for Rewrite {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for JsChange { impl Ord for Rewrite {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get_span().start.cmp(&other.get_span().start) self.get_span().start.cmp(&other.get_span().start)
} }
} }
type Changes<'a> = SmallVec<[&'a str; 8]>;
enum JsChangeInner<'a> {
Wrap {
/// Changes to add before span
before: Changes<'a>,
/// Span to add in between
inner: &'a Span,
/// Changes to add after span
after: Changes<'a>,
},
Replace {
str: Changes<'a>,
},
}
pub(crate) struct JsChangeResult { pub(crate) struct JsChangeResult {
pub js: Vec<u8>, pub js: Vec<u8>,
pub sourcemap: Vec<u8>, pub sourcemap: Vec<u8>,
} }
pub(crate) struct JsChanges { pub(crate) struct JsChanges {
pub inner: Vec<JsChange>, pub inner: Vec<Rewrite>,
} }
impl JsChanges { impl JsChanges {
@ -230,11 +237,11 @@ impl JsChanges {
Self { inner: Vec::new() } Self { inner: Vec::new() }
} }
pub fn add(&mut self, change: JsChange) { pub fn add(&mut self, change: Rewrite) {
self.inner.push(change); self.inner.push(change);
} }
pub fn perform<E>(&mut self, js: &str, cfg: &Config<E>) -> JsChangeResult pub fn perform<E>(&mut self, js: &str, cfg: &Config<E>) -> Result<JsChangeResult, RewriterError>
where where
E: Fn(String) -> String, E: Fn(String) -> String,
E: Clone, E: Clone,
@ -252,13 +259,17 @@ impl JsChanges {
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(js[offset..start].as_bytes()); buffer.extend_from_slice(
js.get(offset..start)
.ok_or_else(|| RewriterError::Oob(offset..start))?
.as_bytes(),
);
let change = change.to_inner(cfg); let inner = change.to_inner(cfg);
match change { match inner {
JsChangeInner::Wrap { JsChangeInner::Wrap {
before, before,
span: wrapspan, inner: wrapspan,
after, after,
} => { } => {
// wrap op // wrap op
@ -268,7 +279,11 @@ impl JsChanges {
let wrapstart = wrapspan.start as usize; let wrapstart = wrapspan.start as usize;
let wrapend = wrapspan.end as usize; let wrapend = wrapspan.end as usize;
buffer.extend_from_slice(js[wrapstart..wrapend].as_bytes()); buffer.extend_from_slice(
js.get(wrapstart..wrapend)
.ok_or_else(|| RewriterError::Oob(wrapstart..wrapend))?
.as_bytes(),
);
for str in after { for str in after {
buffer.extend_from_slice(str.as_bytes()); buffer.extend_from_slice(str.as_bytes());
@ -284,11 +299,16 @@ impl JsChanges {
offset = end; offset = end;
} }
buffer.extend_from_slice(js[offset..].as_bytes()); let js_len = js.len();
buffer.extend_from_slice(
js.get(offset..js_len)
.ok_or_else(|| RewriterError::Oob(offset..js_len))?
.as_bytes(),
);
JsChangeResult { Ok(JsChangeResult {
js: buffer, js: buffer,
sourcemap: map, sourcemap: map,
} })
} }
} }

View file

@ -1,5 +1,7 @@
use std::ops::Range;
use cfg::Config; use cfg::Config;
use changes::{JsChange, JsChangeResult, JsChanges}; use changes::{Rewrite, JsChangeResult, JsChanges};
use oxc::{ use oxc::{
allocator::Allocator, allocator::Allocator,
ast::Visit, ast::Visit,
@ -18,6 +20,8 @@ mod visitor;
pub enum RewriterError { pub enum RewriterError {
#[error("oxc panicked in parser")] #[error("oxc panicked in parser")]
OxcPanicked, OxcPanicked,
#[error("out of bounds while applying range: {0:?})")]
Oob(Range<usize>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -25,7 +29,7 @@ pub struct RewriteResult {
pub js: Vec<u8>, pub js: Vec<u8>,
pub sourcemap: Vec<u8>, pub sourcemap: Vec<u8>,
pub errors: Vec<OxcDiagnostic>, pub errors: Vec<OxcDiagnostic>,
pub changes: Vec<JsChange>, pub changes: Vec<Rewrite>,
} }
pub fn rewrite<E>(js: &str, config: Config<E>) -> Result<RewriteResult, RewriterError> pub fn rewrite<E>(js: &str, config: Config<E>) -> Result<RewriteResult, RewriterError>
@ -57,7 +61,7 @@ where
config, config,
} = visitor; } = visitor;
let JsChangeResult { js, sourcemap } = jschanges.perform(js, &config); let JsChangeResult { js, sourcemap } = jschanges.perform(js, &config)?;
let JsChanges { inner: changes } = jschanges; let JsChanges { inner: changes } = jschanges;
Ok(RewriteResult { Ok(RewriteResult {

View file

@ -15,7 +15,7 @@ use oxc::{
use crate::{ use crate::{
cfg::Config, cfg::Config,
changes::{JsChange, JsChanges}, changes::{Rewrite, JsChanges},
}; };
// 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
@ -58,7 +58,7 @@ where
fn rewrite_ident(&mut self, name: &Atom, span: Span) { fn rewrite_ident(&mut self, name: &Atom, span: Span) {
if UNSAFE_GLOBALS.contains(&name.as_str()) { if UNSAFE_GLOBALS.contains(&name.as_str()) {
self.jschanges.add(JsChange::WrapFn { self.jschanges.add(Rewrite::WrapFn {
span, span,
wrapped: true, wrapped: true,
}); });
@ -83,7 +83,7 @@ where
} }
fn scramitize(&mut self, span: Span) { fn scramitize(&mut self, span: Span) {
self.jschanges.add(JsChange::Scramitize { span }); self.jschanges.add(Rewrite::Scramitize { span });
} }
} }
@ -104,7 +104,7 @@ where
// } else { // } else {
// //
if UNSAFE_GLOBALS.contains(&it.name.as_str()) { if UNSAFE_GLOBALS.contains(&it.name.as_str()) {
self.jschanges.add(JsChange::WrapFn { self.jschanges.add(Rewrite::WrapFn {
span: it.span, span: it.span,
wrapped: false, wrapped: false,
}); });
@ -122,7 +122,7 @@ where
match it { match it {
MemberExpression::StaticMemberExpression(s) => { MemberExpression::StaticMemberExpression(s) => {
if s.property.name == "postMessage" { if s.property.name == "postMessage" {
self.jschanges.add(JsChange::SetRealmFn { self.jschanges.add(Rewrite::SetRealmFn {
span: s.property.span, span: s.property.span,
}); });
@ -159,12 +159,12 @@ where
walk::walk_member_expression(self, it); walk::walk_member_expression(self, it);
} }
fn visit_this_expression(&mut self, it: &ThisExpression) { fn visit_this_expression(&mut self, it: &ThisExpression) {
self.jschanges.add(JsChange::WrapThisFn { span: it.span }); self.jschanges.add(Rewrite::WrapThisFn { span: it.span });
} }
fn visit_debugger_statement(&mut self, it: &DebuggerStatement) { fn visit_debugger_statement(&mut self, it: &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.add(JsChange::Delete { span: it.span }); self.jschanges.add(Rewrite::Delete { span: it.span });
} }
// 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
@ -173,7 +173,7 @@ where
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.add(JsChange::Eval { self.jschanges.add(Rewrite::Eval {
span: Span::new(it.span.start, it.span.end), span: Span::new(it.span.start, it.span.end),
inner: Span::new(s.span.end + 1, it.span.end), inner: Span::new(s.span.end + 1, it.span.end),
}); });
@ -193,14 +193,14 @@ where
fn visit_import_declaration(&mut self, it: &ImportDeclaration<'a>) { fn visit_import_declaration(&mut self, it: &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.add(JsChange::Replace { self.jschanges.add(Rewrite::Replace {
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: &ImportExpression<'a>) { fn visit_import_expression(&mut self, it: &ImportExpression<'a>) {
self.jschanges.add(JsChange::ImportFn { self.jschanges.add(Rewrite::ImportFn {
span: Span::new(it.span.start, it.span.start + 6), span: Span::new(it.span.start, it.span.start + 6),
}); });
walk::walk_import_expression(self, it); walk::walk_import_expression(self, it);
@ -209,7 +209,7 @@ where
fn visit_export_all_declaration(&mut self, it: &ExportAllDeclaration<'a>) { fn visit_export_all_declaration(&mut self, it: &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.add(JsChange::Replace { self.jschanges.add(Rewrite::Replace {
span: it.source.span, span: it.source.span,
text, text,
}); });
@ -218,7 +218,7 @@ where
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.add(JsChange::Replace { self.jschanges.add(Rewrite::Replace {
span: source.span, span: source.span,
text, text,
}); });
@ -234,7 +234,7 @@ where
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.add(JsChange::ScramErr { self.jschanges.add(Rewrite::ScramErr {
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),
name: name.to_compact_str(), name: name.to_compact_str(),
}); });
@ -251,7 +251,7 @@ where
match &p.value { 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.add(JsChange::ShorthandObj { self.jschanges.add(Rewrite::ShorthandObj {
span: s.span, span: s.span,
name: s.name.to_compact_str(), name: s.name.to_compact_str(),
}); });
@ -269,7 +269,7 @@ where
fn visit_function_body(&mut self, it: &FunctionBody<'a>) { fn visit_function_body(&mut self, it: &FunctionBody<'a>) {
// tag function for use in sourcemaps // tag function for use in sourcemaps
if self.config.do_sourcemaps { if self.config.do_sourcemaps {
self.jschanges.add(JsChange::SourceTag { self.jschanges.add(Rewrite::SourceTag {
span: Span::new(it.span.start, it.span.start), span: Span::new(it.span.start, it.span.start),
tagname: it.span.start.to_string(), tagname: it.span.start.to_string(),
}); });
@ -313,7 +313,7 @@ where
fn visit_meta_property(&mut self, it: &MetaProperty<'a>) { fn visit_meta_property(&mut self, it: &MetaProperty<'a>) {
if it.meta.name == "import" { if it.meta.name == "import" {
self.jschanges.add(JsChange::MetaFn { span: it.span }); self.jschanges.add(Rewrite::MetaFn { span: it.span });
} }
} }
@ -321,7 +321,7 @@ where
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.add(JsChange::Assignment { self.jschanges.add(Rewrite::Assignment {
name: s.name.to_compact_str(), name: s.name.to_compact_str(),
entirespan: it.span, entirespan: it.span,
rhsspan: it.right.span(), rhsspan: it.right.span(),