rewriter error handling

This commit is contained in:
Toshit Chawda 2024-10-18 19:58:11 -07:00
parent d2f1e10a6c
commit 6fc473068f
No known key found for this signature in database
GPG key ID: 91480ED99E2B3D9D
4 changed files with 99 additions and 55 deletions

1
rewriter/Cargo.lock generated
View file

@ -999,6 +999,7 @@ dependencies = [
"rand", "rand",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"thiserror",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",

View file

@ -35,6 +35,7 @@ oxc_syntax = "0.20.0"
rand = "0.8.5" rand = "0.8.5"
serde = "1.0.204" serde = "1.0.204"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
thiserror = "1.0.64"
url = "2.5.2" url = "2.5.2"
wasm-bindgen = "0.2.92" wasm-bindgen = "0.2.92"
web-sys = { version = "0.3.72", features = ["Url"] } web-sys = { version = "0.3.72", features = ["Url"] }

View file

@ -4,13 +4,55 @@ use std::{panic, str::FromStr};
use js_sys::{Function, Object, Reflect}; use js_sys::{Function, Object, Reflect};
use rewrite::{rewrite, Config, EncodeFn}; use rewrite::{rewrite, Config, EncodeFn};
use thiserror::Error;
use url::Url; use url::Url;
use wasm_bindgen::{prelude::*, throw_str}; use wasm_bindgen::prelude::*;
#[derive(Debug, Error)]
pub enum RewriterError {
#[error("JS: {0}")]
Js(String),
#[error("URL parse error: {0}")]
Url(#[from] url::ParseError),
#[error("{0} was not {1}")]
Not(String, &'static str),
#[error("❗❗❗ ❗❗❗ REWRITER OFFSET OOB FAIL ❗❗❗ ❗❗❗")]
Oob,
}
impl From<JsValue> for RewriterError {
fn from(value: JsValue) -> Self {
Self::Js(format!("{:?}", value))
}
}
impl From<RewriterError> for JsValue {
fn from(value: RewriterError) -> Self {
JsError::from(value).into()
}
}
impl RewriterError {
fn not_str(x: &str, obj: &JsValue) -> Self {
Self::Not(format!("{:?} in {:?}", x, obj), "string")
}
fn not_fn(obj: JsValue) -> Self {
Self::Not(format!("{:?}", obj), "function")
}
fn not_bool(obj: &JsValue) -> Self {
Self::Not(format!("{:?}", obj), "bool")
}
}
pub type Result<T> = std::result::Result<T, RewriterError>;
#[wasm_bindgen] #[wasm_bindgen]
extern "C" { extern "C" {
#[wasm_bindgen(js_namespace = console)] #[wasm_bindgen(js_namespace = console)]
fn log(s: &str); fn error(s: &str);
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -18,64 +60,62 @@ 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: JsValue) -> EncodeFn { fn create_encode_function(encode: JsValue) -> Result<EncodeFn> {
let Ok(encode) = encode.dyn_into::<Function>() else { let encode = encode.dyn_into::<Function>()?;
throw_str("invalid encode function");
};
Box::new(move |str| { Ok(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_obj(obj: &JsValue, k: &str) -> JsValue { fn get_obj(obj: &JsValue, k: &str) -> Result<JsValue> {
Reflect::get(obj, &k.into()).unwrap() Ok(Reflect::get(obj, &k.into())?)
} }
fn get_str(obj: &JsValue, k: &str) -> String { fn get_str(obj: &JsValue, k: &str) -> Result<String> {
Reflect::get(obj, &k.into()).unwrap().as_string().unwrap() Reflect::get(obj, &k.into())?
.as_string()
.ok_or_else(|| RewriterError::not_str(k, obj))
} }
fn get_flag(scramjet: &Object, url: &str, flag: &str) -> bool { fn get_flag(scramjet: &Object, url: &str, flag: &str) -> Result<bool> {
let fenabled = get_obj(scramjet, "flagEnabled") let fenabled = get_obj(scramjet, "flagEnabled")?
.dyn_into::<Function>() .dyn_into::<Function>()
.unwrap(); .map_err(RewriterError::not_fn)?;
fenabled let ret = fenabled.call2(
.call2(
&JsValue::NULL, &JsValue::NULL,
&flag.into(), &flag.into(),
&web_sys::Url::new(url).expect("invalid url").into(), &web_sys::Url::new(url).expect("invalid url").into(),
) )?;
.expect("error in flagEnabled")
.as_bool() ret.as_bool().ok_or_else(|| RewriterError::not_bool(&ret))
.expect("not bool returned from flagEnabled")
} }
fn get_config(scramjet: &Object, url: &str) -> Config { fn get_config(scramjet: &Object, url: &str) -> Result<Config> {
let codec = &get_obj(scramjet, "codec"); let codec = &get_obj(scramjet, "codec")?;
let config = &get_obj(scramjet, "config"); let config = &get_obj(scramjet, "config")?;
let globals = &get_obj(config, "globals"); let globals = &get_obj(config, "globals")?;
Config { Ok(Config {
prefix: get_str(config, "prefix"), prefix: get_str(config, "prefix")?,
encode: create_encode_function(get_obj(codec, "encode")), encode: create_encode_function(get_obj(codec, "encode")?)?,
wrapfn: get_str(globals, "wrapfn"), wrapfn: get_str(globals, "wrapfn")?,
importfn: get_str(globals, "importfn"), importfn: get_str(globals, "importfn")?,
rewritefn: get_str(globals, "rewritefn"), rewritefn: get_str(globals, "rewritefn")?,
metafn: get_str(globals, "metafn"), metafn: get_str(globals, "metafn")?,
setrealmfn: get_str(globals, "setrealmfn"), setrealmfn: get_str(globals, "setrealmfn")?,
pushsourcemapfn: get_str(globals, "pushsourcemapfn"), pushsourcemapfn: get_str(globals, "pushsourcemapfn")?,
do_sourcemaps: get_flag(scramjet, url, "sourcemaps"), do_sourcemaps: get_flag(scramjet, url, "sourcemaps")?,
capture_errors: get_flag(scramjet, url, "captureErrors"), capture_errors: get_flag(scramjet, url, "captureErrors")?,
scramitize: get_flag(scramjet, url, "scramitize"), scramitize: get_flag(scramjet, url, "scramitize")?,
} })
} }
#[cfg(feature = "drm")] #[cfg(feature = "drm")]
@ -89,17 +129,17 @@ fn drmcheck() -> bool {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn rewrite_js(js: &str, url: &str, scramjet: &Object) -> Vec<u8> { pub fn rewrite_js(js: &str, url: &str, scramjet: &Object) -> Result<Vec<u8>> {
#[cfg(feature = "drm")] #[cfg(feature = "drm")]
if !drmcheck() { if !drmcheck() {
return Vec::new(); return Vec::new();
} }
rewrite(js, Url::from_str(url).unwrap(), get_config(scramjet, url)) rewrite(js, Url::from_str(url)?, get_config(scramjet, url)?)
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, scramjet: &Object) -> Vec<u8> { pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, scramjet: &Object) -> Result<Vec<u8>> {
#[cfg(feature = "drm")] #[cfg(feature = "drm")]
if !drmcheck() { if !drmcheck() {
return Vec::new(); return Vec::new();
@ -108,5 +148,5 @@ pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, scramjet: &Object) -> V
// 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(scramjet, url)) rewrite(js, Url::from_str(url)?, get_config(scramjet, url)?)
} }

View file

@ -1,6 +1,7 @@
use core::str; use core::str;
use std::str::from_utf8; use std::str::from_utf8;
use crate::{error, Result, RewriterError};
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{
@ -443,21 +444,20 @@ fn random_string() -> String {
.to_string() .to_string()
} }
pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> { pub fn rewrite(js: &str, url: Url, config: Config) -> Result<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 err in ret.errors {
let cloned = js.to_string(); let cloned = js.to_string();
let error = error.with_source_code(cloned); let err = err.with_source_code(cloned);
println!("{error:?}"); println!("oxc parse error {err:?}");
error(&format!("oxc parse error {err:?}"))
} }
let program = ret.program; let program = ret.program;
// dbg!(&program);
let sourcetag = random_string(); let sourcetag = random_string();
let mut ast_pass = Rewriter { let mut ast_pass = Rewriter {
@ -540,7 +540,8 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> {
); );
} }
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes()); buffer
.extend_from_slice(js.get(offset..start).ok_or(RewriterError::Oob)?.as_bytes());
buffer.extend_from_slice(text.as_bytes()); buffer.extend_from_slice(text.as_bytes());
offset = end; offset = end;
@ -570,7 +571,8 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> {
} }
JsChange::SourceTag { tagstart } => { JsChange::SourceTag { tagstart } => {
let start = *tagstart as usize; let start = *tagstart as usize;
buffer.extend_from_slice(unsafe { js.get_unchecked(offset..start) }.as_bytes()); buffer
.extend_from_slice(js.get(offset..start).ok_or(RewriterError::Oob)?.as_bytes());
let inject = format!("/*scramtag {} {}*/", start, sourcetag); let inject = format!("/*scramtag {} {}*/", start, sourcetag);
buffer.extend_from_slice(inject.as_bytes()); buffer.extend_from_slice(inject.as_bytes());
@ -589,10 +591,10 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec<u8> {
sourcemap.extend_from_slice(&buffer); sourcemap.extend_from_slice(&buffer);
return sourcemap; return Ok(sourcemap);
} }
buffer Ok(buffer)
} }
fn json_escape_string(s: &str) -> String { fn json_escape_string(s: &str) -> String {