rewrite... TWO!!

This commit is contained in:
Toshit Chawda 2024-12-13 18:46:58 -08:00
parent 6d03da6d85
commit 6edad29dbb
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
9 changed files with 273 additions and 135 deletions

View file

@ -65,8 +65,6 @@ fn main() -> Result<()> {
eprintln!("{}", err.with_source_code(source.clone()));
}
println!("changes: {:#?}", res.changes);
println!(
"rewritten:\n{}",
String::from_utf8(res.js).context("failed to parse rewritten js")?
@ -160,6 +158,10 @@ function $wrap(val) {
return val;
}
const $gwrap = $wrap;
function $scramitize(val) { return val }
function assert(val) {
if (!val) fail();
}
@ -172,7 +174,7 @@ function check(val) {
.unwrap();
let rewritten = dorewrite(&content).unwrap();
println!("{:?}", rewritten);
println!("{}", std::str::from_utf8(&rewritten.js).unwrap());
context.eval(Source::from_bytes(&rewritten.js)).unwrap();
println!("PASS");

View file

@ -0,0 +1,5 @@
check(window);
check(this);
check(globalThis);
check(location);
check(globalThis["win" + "dow"])

View file

@ -0,0 +1,3 @@
const { location: x } = globalThis;
check(x);

View file

@ -0,0 +1,6 @@
function f(g = globalThis, l = location) {
check(g);
check(l);
}
f();

View file

@ -0,0 +1,7 @@
function f(location, globalThis) {
assert(location === 1)
assert(globalThis === 2)
}
f(1, 2);

View file

@ -0,0 +1,8 @@
let reached = false
let eval = (t) => t === "location = 1" && (reached = true)
// testing to make sure this doesn't get rewritten
eval("location = 1")
if (!reached) fail();

View file

@ -0,0 +1,3 @@
for (location of ["https://google.com"]) {
//
}

View file

@ -7,7 +7,7 @@ use smallvec::{smallvec, SmallVec};
use crate::{cfg::Config, RewriterError};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Rewrite {
pub(crate) enum Rewrite {
/// `(cfg.wrapfn(ident))` | `cfg.wrapfn(ident)`
WrapFn {
span: Span,
@ -52,7 +52,7 @@ pub enum Rewrite {
rhsspan: Span,
op: AssignmentOperator,
},
/// `ident,` -> `ident: cfg.wrapfn(ident)`
/// `ident,` -> `ident: cfg.wrapfn(ident),`
ShorthandObj {
span: Span,
name: CompactStr,
@ -73,93 +73,228 @@ pub enum Rewrite {
}
impl Rewrite {
pub fn get_span(&self) -> &Span {
fn into_inner(self) -> SmallVec<[JsChange; 4]> {
match self {
Self::WrapFn { span, .. } => span,
Self::SetRealmFn { span, .. } => span,
Self::WrapThisFn { span } => span,
Self::ImportFn { span } => span,
Self::MetaFn { span } => span,
Self::WrapFn { wrapped, span } => {
let start = Span::new(span.start, span.start);
let end = Span::new(span.end, span.end);
if wrapped {
smallvec![
JsChange::WrapFn {
span: start,
extra: true
},
JsChange::DoubleClosingParen { span: end }
]
} else {
smallvec![
JsChange::WrapFn {
span: start,
extra: false
},
JsChange::ClosingParen {
span: end,
semi: false,
}
]
}
}
Self::SetRealmFn { span } => smallvec![JsChange::SetRealmFn { span }],
Self::WrapThisFn { span } => smallvec![
JsChange::WrapThisFn {
span: Span::new(span.start, span.start)
},
JsChange::ClosingParen {
span: Span::new(span.end, span.end),
semi: false,
}
],
Self::ImportFn { span } => smallvec![JsChange::ImportFn { span }],
Self::MetaFn { span } => smallvec![JsChange::MetaFn { span }],
Self::ScramErr { span, .. } => span,
Self::Scramitize { span } => span,
Self::ScramErr { span, .. } => {
smallvec![
JsChange::ScramErrFn {
span: Span::new(span.start, span.start)
},
JsChange::ClosingParen {
span: Span::new(span.end, span.end),
semi: true
}
]
}
Self::Scramitize { span } => {
smallvec![
JsChange::ScramitizeFn {
span: Span::new(span.start, span.start)
},
JsChange::ClosingParen {
span: Span::new(span.end, span.end),
semi: false,
}
]
}
Self::Eval { span, .. } => span,
Self::Assignment { entirespan, .. } => entirespan,
Self::ShorthandObj { span, .. } => span,
Self::SourceTag { span, .. } => span,
Self::Replace { span, .. } => span,
Self::Delete { span } => span,
Self::Eval { inner, span } => smallvec![
JsChange::EvalRewriteFn {
span: Span::new(span.start, inner.start)
},
JsChange::ReplaceClosingParen {
span: Span::new(inner.end, span.end),
}
],
Self::Assignment {
name,
rhsspan,
op,
entirespan,
} => smallvec![
JsChange::AssignmentLeft {
name,
op,
span: Span::new(entirespan.start, rhsspan.start)
},
JsChange::ReplaceClosingParen {
span: Span::new(rhsspan.end, entirespan.end)
}
],
// maps to insert
Self::ShorthandObj { name, span } => smallvec![JsChange::ShorthandObj {
ident: name,
span: Span::new(span.end, span.end)
}],
// maps to insert
Self::SourceTag { tagname, span } => smallvec![JsChange::SourceTag { span, tagname }],
Self::Replace { text, span } => smallvec![JsChange::Replace { span, text }],
Self::Delete { span } => smallvec![JsChange::Delete { span }],
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
enum JsChange {
/// insert `${cfg.wrapfn}(`
WrapFn { span: Span, extra: bool },
/// insert `${cfg.setrealmfn}({}).`
SetRealmFn { span: Span },
/// insert `${cfg.wrapthis}(`
WrapThisFn { span: Span },
/// insert `$scramerr(`
ScramErrFn { span: Span },
/// insert `$scramitize(`
ScramitizeFn { span: Span },
/// insert `eval(${cfg.rewritefn}(`
EvalRewriteFn { span: Span },
/// insert `: ${cfg.wrapfn}(ident)`
ShorthandObj { span: Span, ident: CompactStr },
/// insert scramtag
SourceTag { span: Span, tagname: String },
/// replace span with `(${cfg.importfn}("${cfg.base}")`
ImportFn { span: Span },
/// replace span with `${cfg.metafn}("${cfg.base}")`
MetaFn { span: Span },
/// replace span with `((t)=>$scramjet$tryset(${name},"${op}",t)||(${name}${op}t))(`
AssignmentLeft {
span: Span,
name: CompactStr,
op: AssignmentOperator,
},
/// replace span with `)`
ReplaceClosingParen { span: Span },
/// insert `)`
ClosingParen { span: Span, semi: bool },
/// insert `))`
DoubleClosingParen { span: Span },
/// replace span with text
Replace { span: Span, text: String },
/// replace span with ""
Delete { span: Span },
}
impl JsChange {
fn get_span(&self) -> &Span {
match self {
Self::WrapFn { span, .. }
| Self::SetRealmFn { span }
| Self::WrapThisFn { span }
| Self::ScramErrFn { span }
| Self::ScramitizeFn { span }
| Self::EvalRewriteFn { span }
| Self::ShorthandObj { span, .. }
| Self::SourceTag { span, .. }
| Self::ImportFn { span }
| Self::MetaFn { span }
| Self::AssignmentLeft { span, .. }
| Self::ReplaceClosingParen { span }
| Self::ClosingParen { span, .. }
| Self::DoubleClosingParen { span }
| Self::Replace { span, .. }
| Self::Delete { span } => span,
}
}
// returns (bunch of stuff to add before, option<bunch of stuff to add after>)
// bunch of stuff to add after should only be some if it's not a replace op
fn to_inner<'a, E>(&'a self, cfg: &'a Config<E>) -> JsChangeInner<'a>
where
E: Fn(String) -> String,
E: Clone,
{
match self {
Self::WrapFn { wrapped, span } => {
if *wrapped {
JsChangeInner::Wrap {
before: smallvec!["(", cfg.wrapfn.as_str(), "("],
inner: span,
after: smallvec!["))"],
Self::WrapFn { span, extra } => {
if *extra {
JsChangeInner::Insert {
loc: span.start,
str: smallvec!["(", cfg.wrapfn.as_str(), "("],
}
} else {
JsChangeInner::Wrap {
before: smallvec![cfg.wrapfn.as_str(), "("],
inner: span,
after: smallvec![")"],
JsChangeInner::Insert {
loc: span.start,
str: smallvec![cfg.wrapfn.as_str(), "("],
}
}
}
Self::SetRealmFn { span } => JsChangeInner::Wrap {
before: smallvec![cfg.setrealmfn.as_str(), "({})."],
inner: span,
after: smallvec![],
Self::SetRealmFn { span } => JsChangeInner::Insert {
loc: span.start,
str: smallvec![cfg.setrealmfn.as_str(), "({})."],
},
Self::WrapThisFn { span } => JsChangeInner::Wrap {
before: smallvec![cfg.wrapthisfn.as_str(), "("],
inner: span,
after: smallvec![")"],
Self::WrapThisFn { span } => JsChangeInner::Insert {
loc: span.start,
str: smallvec![cfg.wrapthisfn.as_str(), "("],
},
Self::ImportFn { .. } => JsChangeInner::Replace(smallvec![
"(",
cfg.importfn.as_str(),
"(\"",
cfg.base.as_str(),
"\"))"
]),
Self::MetaFn { .. } => JsChangeInner::Replace(smallvec![
cfg.metafn.as_str(),
"(\"",
cfg.base.as_str(),
"\")"
]),
// maps to insert
Self::ScramErr { name, .. } => {
JsChangeInner::Replace(smallvec!["$scramerr(", name.as_str(), ");"])
}
Self::Scramitize { span } => JsChangeInner::Wrap {
before: smallvec!["$scramitize("],
inner: span,
after: smallvec![")"],
Self::ScramErrFn { span } => JsChangeInner::Insert {
loc: span.start,
str: smallvec!["$scramerr("],
},
Self::Eval { inner, .. } => JsChangeInner::Wrap {
before: smallvec!["eval(", cfg.rewritefn.as_str(), "("],
inner,
after: smallvec![")"],
Self::ScramitizeFn { span } => JsChangeInner::Insert {
loc: span.start,
str: smallvec!["$scramitize("],
},
Self::Assignment {
name, rhsspan, op, ..
} => JsChangeInner::Wrap {
before: smallvec![
Self::EvalRewriteFn { .. } => JsChangeInner::Replace {
str: smallvec!["eval(", cfg.rewritefn.as_str(), "("],
},
Self::ShorthandObj { span, ident } => JsChangeInner::Insert {
loc: span.start,
str: smallvec![":", cfg.wrapfn.as_str(), "(", ident.as_str(), ")"],
},
Self::SourceTag { span, tagname } => JsChangeInner::Insert {
loc: span.start,
str: smallvec![
"/*scramtag ",
cfg.sourcetag.as_str(),
tagname.as_str(),
"*/"
],
},
Self::ImportFn { span } => JsChangeInner::Replace {
str: smallvec!["(", cfg.importfn.as_str(), "(\"", cfg.base.as_str(), "\")"],
},
Self::MetaFn { span } => JsChangeInner::Replace {
str: smallvec![cfg.metafn.as_str(), "(\"", cfg.base.as_str()],
},
Self::AssignmentLeft { span, name, op } => JsChangeInner::Replace {
str: smallvec![
"((t)=>$scramjet$tryset(",
name.as_str(),
",\"",
@ -167,41 +302,39 @@ impl Rewrite {
"\",t)||(",
name.as_str(),
op.as_str(),
"t))("
"t)("
],
inner: rhsspan,
after: smallvec![")"],
},
// maps to insert
Self::ShorthandObj { name, .. } => JsChangeInner::Replace(smallvec![
name.as_str(),
":",
cfg.wrapfn.as_str(),
"(",
name.as_str(),
")"
]),
// maps to insert
Self::SourceTag { tagname, .. } => JsChangeInner::Replace(smallvec![
"/*scramtag ",
tagname.as_str(),
" ",
cfg.sourcetag.as_str(),
"*/"
]),
Self::Replace { text, .. } => JsChangeInner::Replace(smallvec![text.as_str()]),
Self::Delete { .. } => JsChangeInner::Replace(smallvec![]),
Self::ReplaceClosingParen { span } => JsChangeInner::Replace {
str: smallvec![")"],
},
Self::ClosingParen { span, semi } => JsChangeInner::Insert {
loc: span.start,
str: if *semi {
smallvec![");"]
} else {
smallvec![")"]
},
},
Self::DoubleClosingParen { span } => JsChangeInner::Insert {
loc: span.start,
str: smallvec!["))"],
},
Self::Replace { span, text } => JsChangeInner::Replace {
str: smallvec![text.as_str()],
},
Self::Delete { span } => JsChangeInner::Replace { str: smallvec![""] },
}
}
}
impl PartialOrd for Rewrite {
impl PartialOrd for JsChange {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Rewrite {
impl Ord for JsChange {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get_span().start.cmp(&other.get_span().start)
}
@ -210,17 +343,8 @@ impl Ord for Rewrite {
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>,
},
Insert { loc: u32, str: Changes<'a> },
Replace { str: Changes<'a> },
}
pub(crate) struct JsChangeResult {
@ -229,7 +353,7 @@ pub(crate) struct JsChangeResult {
}
pub(crate) struct JsChanges {
pub inner: Vec<Rewrite>,
pub inner: Vec<JsChange>,
}
impl JsChanges {
@ -238,7 +362,7 @@ impl JsChanges {
}
pub fn add(&mut self, change: Rewrite) {
self.inner.push(change);
self.inner.extend(change.into_inner().into_iter());
}
pub fn perform<E>(&mut self, js: &str, cfg: &Config<E>) -> Result<JsChangeResult, RewriterError>
@ -267,30 +391,13 @@ impl JsChanges {
let inner = change.to_inner(cfg);
match inner {
JsChangeInner::Wrap {
before,
inner: wrapspan,
after,
} => {
// wrap op
for str in before {
buffer.extend_from_slice(str.as_bytes());
}
let wrapstart = wrapspan.start as usize;
let wrapend = wrapspan.end as usize;
buffer.extend_from_slice(
js.get(wrapstart..wrapend)
.ok_or_else(|| RewriterError::Oob(wrapstart..wrapend))?
.as_bytes(),
);
for str in after {
JsChangeInner::Insert { loc, str } => {
for str in str {
buffer.extend_from_slice(str.as_bytes());
}
}
JsChangeInner::Replace(list) => {
for str in list {
JsChangeInner::Replace { str } => {
for str in str {
buffer.extend_from_slice(str.as_bytes());
}
}

View file

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