mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-14 15:00:01 -04:00
rewrite... TWO!!
This commit is contained in:
parent
6d03da6d85
commit
6edad29dbb
9 changed files with 273 additions and 135 deletions
|
@ -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");
|
||||
|
|
5
rewriter/native/tests/0-sanity.js
Normal file
5
rewriter/native/tests/0-sanity.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
check(window);
|
||||
check(this);
|
||||
check(globalThis);
|
||||
check(location);
|
||||
check(globalThis["win" + "dow"])
|
3
rewriter/native/tests/1-destructure.js
Normal file
3
rewriter/native/tests/1-destructure.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
const { location: x } = globalThis;
|
||||
|
||||
check(x);
|
6
rewriter/native/tests/2-function-default.js
Normal file
6
rewriter/native/tests/2-function-default.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
function f(g = globalThis, l = location) {
|
||||
check(g);
|
||||
check(l);
|
||||
}
|
||||
|
||||
f();
|
7
rewriter/native/tests/3-shadow-globals.js
Normal file
7
rewriter/native/tests/3-shadow-globals.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
function f(location, globalThis) {
|
||||
assert(location === 1)
|
||||
assert(globalThis === 2)
|
||||
}
|
||||
|
||||
f(1, 2);
|
|
@ -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();
|
3
rewriter/native/tests/for-assignment.js.disabled
Normal file
3
rewriter/native/tests/for-assignment.js.disabled
Normal file
|
@ -0,0 +1,3 @@
|
|||
for (location of ["https://google.com"]) {
|
||||
//
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue