use std::{
fmt::Debug,
io::{self, Write},
};
use anyhow::{Result, bail};
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
pub(crate) struct JSIntrinsics: u32 {
const DATE = 1;
const EVAL = 1 << 1;
const REGEXP_COMPILER = 1 << 2;
const REGEXP = 1 << 3;
const JSON = 1 << 4;
const PROXY = 1 << 5;
const MAP_SET = 1 << 6;
const TYPED_ARRAY = 1 << 7;
const PROMISE = 1 << 8;
const OPERATORS = 1 << 12;
const BIGNUM_EXTENSION = 1 << 13;
const TEXT_ENCODING = 1 << 14;
const WEAK_REF = 1 << 16;
const PERFORMANCE = 1 << 17;
}
}
bitflags! {
#[derive(Debug)]
pub(crate) struct JavyIntrinsics: u32 {
const STREAM_IO = 1;
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum BytecodeStripping {
None,
#[default]
Source,
SourceAndDebug,
}
pub struct Config {
pub(crate) intrinsics: JSIntrinsics,
pub(crate) javy_intrinsics: JavyIntrinsics,
pub(crate) simd_json_builtins: bool,
pub(crate) gc_threshold: usize,
pub(crate) memory_limit: usize,
pub(crate) max_stack_size: usize,
pub(crate) log_stream: Box<dyn Write>,
pub(crate) err_stream: Box<dyn Write>,
pub(crate) bytecode_stripping: BytecodeStripping,
}
impl Default for Config {
fn default() -> Self {
let mut intrinsics = JSIntrinsics::all();
intrinsics.set(JSIntrinsics::TEXT_ENCODING, false);
intrinsics.set(JSIntrinsics::WEAK_REF, false);
intrinsics.set(JSIntrinsics::PERFORMANCE, false);
Self {
intrinsics,
javy_intrinsics: JavyIntrinsics::empty(),
simd_json_builtins: false,
gc_threshold: usize::MAX,
memory_limit: usize::MAX,
max_stack_size: 256 * 1024, log_stream: Box::new(std::io::stdout()),
err_stream: Box::new(std::io::stderr()),
bytecode_stripping: BytecodeStripping::default(),
}
}
}
impl Config {
pub fn date(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::DATE, enable);
self
}
pub fn eval(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::EVAL, enable);
self
}
pub fn regexp_compiler(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::REGEXP_COMPILER, enable);
self
}
pub fn regexp(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::REGEXP, enable);
self
}
pub fn json(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::JSON, enable);
self
}
pub fn proxy(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::PROXY, enable);
self
}
pub fn map_set(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::MAP_SET, enable);
self
}
pub fn promise(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::PROMISE, enable);
self
}
pub fn operator_overloading(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::OPERATORS, enable);
self
}
pub fn bignum_extension(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::BIGNUM_EXTENSION, enable);
self
}
pub fn text_encoding(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::TEXT_ENCODING, enable);
self
}
pub fn javy_stream_io(&mut self, enable: bool) -> &mut Self {
self.javy_intrinsics.set(JavyIntrinsics::STREAM_IO, enable);
self
}
pub fn redirect_stdout_to_stderr(&mut self, enable: bool) -> &mut Self {
self.log_stream = if enable {
Box::new(io::stderr())
} else {
Box::new(io::stdout())
};
self
}
#[cfg(feature = "json")]
pub fn simd_json_builtins(&mut self, enable: bool) -> &mut Self {
self.simd_json_builtins = enable;
self
}
pub fn gc_threshold(&mut self, bytes: usize) -> &mut Self {
self.gc_threshold = bytes;
self
}
pub fn memory_limit(&mut self, bytes: usize) -> &mut Self {
self.memory_limit = bytes;
self
}
pub fn max_stack_size(&mut self, bytes: usize) -> &mut Self {
self.max_stack_size = bytes;
self
}
pub fn log_stream(&mut self, stream: Box<dyn Write>) -> &mut Self {
self.log_stream = stream;
self
}
pub fn err_stream(&mut self, stream: Box<dyn Write>) -> &mut Self {
self.err_stream = stream;
self
}
pub fn bytecode_stripping(&mut self, stripping: BytecodeStripping) -> &mut Self {
self.bytecode_stripping = stripping;
self
}
pub fn weak_ref(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::WEAK_REF, enable);
self
}
pub fn performance(&mut self, enable: bool) -> &mut Self {
self.intrinsics.set(JSIntrinsics::PERFORMANCE, enable);
self
}
pub(crate) fn validate(self) -> Result<Self> {
if self.simd_json_builtins && !self.intrinsics.contains(JSIntrinsics::JSON) {
bail!("JSON Intrinsic is required to override JSON.parse and JSON.stringify");
}
Ok(self)
}
}
#[cfg(test)]
mod tests {
use super::{BytecodeStripping, Config};
#[test]
fn bytecode_stripping_defaults_and_configuration() {
let mut config = Config::default();
assert_eq!(config.bytecode_stripping, BytecodeStripping::Source);
config.bytecode_stripping(BytecodeStripping::SourceAndDebug);
assert_eq!(config.bytecode_stripping, BytecodeStripping::SourceAndDebug);
}
#[cfg(feature = "json")]
#[test]
fn err_config_validation() {
let mut config = Config::default();
config.simd_json_builtins(true);
config.json(false);
assert!(config.validate().is_err());
}
#[cfg(feature = "json")]
#[test]
fn ok_config_validation() {
let mut config = Config::default();
config.simd_json_builtins(true);
assert!(config.validate().is_ok());
}
}