jsfuck/src/main.rs

170 lines
4.8 KiB
Rust

#![warn(clippy::pedantic)]
use std::io::{stdin, Read};
fn main() {
let mut input = String::new();
stdin().read_to_string(&mut input).unwrap();
println!(
"{}({})()",
function_constructor(),
string_with_fromcodepoint(input.trim())
);
}
fn string_with_fromcodepoint(mut s: &str) -> String {
const ARG_LIMIT: usize = u16::MAX as usize - 3000; // without this 3000, it sometimes throws a call stack overflow error
if s.is_empty() {
return "[]+[]".to_owned();
}
let mut out = String::new();
loop {
if s.is_empty() {
break;
}
let split_point = if s.len() < ARG_LIMIT {
s.len()
} else {
let mut i = ARG_LIMIT;
loop {
if s.is_char_boundary(i) {
break i;
}
i -= 1;
}
};
out.push_str(&short_string_with_fromcodepoint(&s[..split_point]));
out.push('+');
s = &s[split_point..];
}
out.pop(); // remove the last `+`
out
}
fn short_string_with_fromcodepoint(s: &str) -> String {
let mut out = String::new();
out.push_str(&function_constructor());
out.push('(');
out.push_str(&string("return String.fromCodePoint("));
for c in s.chars() {
out.push('+');
out.push_str(&string(&(c as u32).to_string()));
out.push('+');
out.push_str(&char(','));
}
out.push('+');
out.push_str(&char(')'));
out.push_str(")()");
out
}
fn regexp_constructor() -> String {
format!("{}({})()", function_constructor(), string("return RegExp"))
}
fn function_constructor() -> String {
format!("[][{}][{}]", string("flat"), string("constructor"))
}
fn string(s: &str) -> String {
s.chars().map(char).collect::<Vec<_>>().join("+")
}
fn char(c: char) -> String {
if c == ',' {
format!("([[]][{}]([[]])+[])", string("concat"))
} else if c.is_ascii() && c.is_numeric() {
format!("(({})+[])", number(c as usize - b'0' as usize))
} else if let Some(index) = "undefined".find(c) {
format!("([][[]]+[])[{}]", number(index))
} else if let Some(index) = "true".find(c) {
format!("(!![]+[])[{}]", number(index))
} else if let Some(index) = "false".find(c) {
format!("(![]+[])[{}]", number(index))
} else if let Some(index) = "function flat() {".find(c) {
format!("([][{}]+[])[{}]", string("flat"), number(index))
} else if let Some(index) = "[object Array Iterator]".find(c) {
format!("([][{}]()+[])[{}]", string("entries"), number(index))
} else if let Some(index) = "Number".find(c) {
format!(
"((+[])[{}]+[])[{}]",
string("constructor"),
number(index + 9)
)
} else if let Some(index) = "String".find(c) {
format!(
"(([]+[])[{}]+[])[{}]",
string("constructor"),
number(index + 9)
)
} else if let Some(index) = "Function".find(c) {
format!("({}+[])[{}]", function_constructor(), number(index + 9))
} else if c == '}' {
format!(
"([][{}]+[])[{}](-({}))",
string("flat"),
string("at"),
number(1)
)
} else if c == '.' {
format!("(+({})+[])[{}]", string("11e100"), number(1))
} else if c.is_ascii_lowercase() {
format!(
"({})[{}+(([]+[])[{}][{}])]({})",
number(c as usize - b'a' as usize + 10),
string("to"),
string("constructor"),
string("name"),
number(36)
)
} else if let Some(index) = "RangeError:".find(c) {
format!(
"({}({})()+[])[{}]",
function_constructor(),
string("try{String().normalize(false)}catch(f){return f}"),
number(index)
)
} else if let Some(index) = "/(?:)/".find(c) {
format!("({}()+[])[{}]", regexp_constructor(), number(index))
} else if c == '\\' {
format!(
"({}({})+[])[{}]",
regexp_constructor(),
string("/"),
number(1)
)
} else if c == '\'' {
format!(
"({}({})()+[])[{}]",
function_constructor(),
string("try{Function([[]].concat([[]]).toString())}catch(f){return f}"),
number(30)
)
} else {
let mut buf = [0; 2];
let buf = c.encode_utf16(&mut buf);
let s = buf
.iter()
.map(|v| format!("\\u{:04x}", v))
.collect::<String>();
format!(
"{}({})()",
function_constructor(),
string(&format!("return '{}'", s))
)
}
}
fn number(n: usize) -> String {
if n == 0 {
"+[]".to_owned()
} else {
"+!![]".repeat(n)
}
}