From 6fc473068f6cece70b471f97b8b96d0d9f7241a1 Mon Sep 17 00:00:00 2001 From: Toshit Chawda Date: Fri, 18 Oct 2024 19:58:11 -0700 Subject: [PATCH] rewriter error handling --- rewriter/Cargo.lock | 1 + rewriter/Cargo.toml | 1 + rewriter/src/lib.rs | 130 ++++++++++++++++++++++++++-------------- rewriter/src/rewrite.rs | 22 +++---- 4 files changed, 99 insertions(+), 55 deletions(-) diff --git a/rewriter/Cargo.lock b/rewriter/Cargo.lock index 12c2c8e..8f4c86f 100644 --- a/rewriter/Cargo.lock +++ b/rewriter/Cargo.lock @@ -999,6 +999,7 @@ dependencies = [ "rand", "serde", "serde-wasm-bindgen", + "thiserror", "url", "wasm-bindgen", "web-sys", diff --git a/rewriter/Cargo.toml b/rewriter/Cargo.toml index 31e81ec..9b0010b 100644 --- a/rewriter/Cargo.toml +++ b/rewriter/Cargo.toml @@ -35,6 +35,7 @@ oxc_syntax = "0.20.0" rand = "0.8.5" serde = "1.0.204" serde-wasm-bindgen = "0.6.5" +thiserror = "1.0.64" url = "2.5.2" wasm-bindgen = "0.2.92" web-sys = { version = "0.3.72", features = ["Url"] } diff --git a/rewriter/src/lib.rs b/rewriter/src/lib.rs index b32487f..99762aa 100644 --- a/rewriter/src/lib.rs +++ b/rewriter/src/lib.rs @@ -4,13 +4,55 @@ use std::{panic, str::FromStr}; use js_sys::{Function, Object, Reflect}; use rewrite::{rewrite, Config, EncodeFn}; +use thiserror::Error; 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 for RewriterError { + fn from(value: JsValue) -> Self { + Self::Js(format!("{:?}", value)) + } +} + +impl From 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 = std::result::Result; #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); + fn error(s: &str); } #[wasm_bindgen] @@ -18,64 +60,62 @@ pub fn init() { panic::set_hook(Box::new(console_error_panic_hook::hook)); } -fn create_encode_function(encode: JsValue) -> EncodeFn { - let Ok(encode) = encode.dyn_into::() else { - throw_str("invalid encode function"); - }; +fn create_encode_function(encode: JsValue) -> Result { + let encode = encode.dyn_into::()?; - Box::new(move |str| { + Ok(Box::new(move |str| { encode .call1(&JsValue::NULL, &str.into()) .unwrap() .as_string() .unwrap() .to_string() - }) + })) } -fn get_obj(obj: &JsValue, k: &str) -> JsValue { - Reflect::get(obj, &k.into()).unwrap() +fn get_obj(obj: &JsValue, k: &str) -> Result { + Ok(Reflect::get(obj, &k.into())?) } -fn get_str(obj: &JsValue, k: &str) -> String { - Reflect::get(obj, &k.into()).unwrap().as_string().unwrap() +fn get_str(obj: &JsValue, k: &str) -> Result { + 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 { - let fenabled = get_obj(scramjet, "flagEnabled") +fn get_flag(scramjet: &Object, url: &str, flag: &str) -> Result { + let fenabled = get_obj(scramjet, "flagEnabled")? .dyn_into::() - .unwrap(); - fenabled - .call2( - &JsValue::NULL, - &flag.into(), - &web_sys::Url::new(url).expect("invalid url").into(), - ) - .expect("error in flagEnabled") - .as_bool() - .expect("not bool returned from flagEnabled") + .map_err(RewriterError::not_fn)?; + let ret = fenabled.call2( + &JsValue::NULL, + &flag.into(), + &web_sys::Url::new(url).expect("invalid url").into(), + )?; + + ret.as_bool().ok_or_else(|| RewriterError::not_bool(&ret)) } -fn get_config(scramjet: &Object, url: &str) -> Config { - let codec = &get_obj(scramjet, "codec"); - let config = &get_obj(scramjet, "config"); - let globals = &get_obj(config, "globals"); +fn get_config(scramjet: &Object, url: &str) -> Result { + let codec = &get_obj(scramjet, "codec")?; + let config = &get_obj(scramjet, "config")?; + let globals = &get_obj(config, "globals")?; - Config { - prefix: get_str(config, "prefix"), - encode: create_encode_function(get_obj(codec, "encode")), + Ok(Config { + prefix: get_str(config, "prefix")?, + encode: create_encode_function(get_obj(codec, "encode")?)?, - wrapfn: get_str(globals, "wrapfn"), - importfn: get_str(globals, "importfn"), - rewritefn: get_str(globals, "rewritefn"), - metafn: get_str(globals, "metafn"), - setrealmfn: get_str(globals, "setrealmfn"), - pushsourcemapfn: get_str(globals, "pushsourcemapfn"), + wrapfn: get_str(globals, "wrapfn")?, + importfn: get_str(globals, "importfn")?, + rewritefn: get_str(globals, "rewritefn")?, + metafn: get_str(globals, "metafn")?, + setrealmfn: get_str(globals, "setrealmfn")?, + pushsourcemapfn: get_str(globals, "pushsourcemapfn")?, - do_sourcemaps: get_flag(scramjet, url, "sourcemaps"), - capture_errors: get_flag(scramjet, url, "captureErrors"), - scramitize: get_flag(scramjet, url, "scramitize"), - } + do_sourcemaps: get_flag(scramjet, url, "sourcemaps")?, + capture_errors: get_flag(scramjet, url, "captureErrors")?, + scramitize: get_flag(scramjet, url, "scramitize")?, + }) } #[cfg(feature = "drm")] @@ -89,17 +129,17 @@ fn drmcheck() -> bool { } #[wasm_bindgen] -pub fn rewrite_js(js: &str, url: &str, scramjet: &Object) -> Vec { +pub fn rewrite_js(js: &str, url: &str, scramjet: &Object) -> Result> { #[cfg(feature = "drm")] if !drmcheck() { 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] -pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, scramjet: &Object) -> Vec { +pub fn rewrite_js_from_arraybuffer(js: &[u8], url: &str, scramjet: &Object) -> Result> { #[cfg(feature = "drm")] if !drmcheck() { 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 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)?) } diff --git a/rewriter/src/rewrite.rs b/rewriter/src/rewrite.rs index a5c5757..9805dc3 100644 --- a/rewriter/src/rewrite.rs +++ b/rewriter/src/rewrite.rs @@ -1,6 +1,7 @@ use core::str; use std::str::from_utf8; +use crate::{error, Result, RewriterError}; use oxc_allocator::Allocator; use oxc_ast::{ ast::{ @@ -443,21 +444,20 @@ fn random_string() -> String { .to_string() } -pub fn rewrite(js: &str, url: Url, config: Config) -> Vec { +pub fn rewrite(js: &str, url: Url, config: Config) -> Result> { let allocator = Allocator::default(); let source_type = SourceType::default(); 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 error = error.with_source_code(cloned); - println!("{error:?}"); + let err = err.with_source_code(cloned); + println!("oxc parse error {err:?}"); + error(&format!("oxc parse error {err:?}")) } let program = ret.program; - // dbg!(&program); - let sourcetag = random_string(); let mut ast_pass = Rewriter { @@ -540,7 +540,8 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec { ); } - 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()); offset = end; @@ -570,7 +571,8 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec { } JsChange::SourceTag { tagstart } => { 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); buffer.extend_from_slice(inject.as_bytes()); @@ -589,10 +591,10 @@ pub fn rewrite(js: &str, url: Url, config: Config) -> Vec { sourcemap.extend_from_slice(&buffer); - return sourcemap; + return Ok(sourcemap); } - buffer + Ok(buffer) } fn json_escape_string(s: &str) -> String {