use rusty_v8 as v8;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum V8HeapPreset {
Minimal,
Small,
Default,
Large,
Maximum,
Custom(V8HeapConfig),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct V8HeapConfig {
pub initial_heap_size_mb: usize,
pub max_heap_size_mb: usize,
pub initial_old_space_size_mb: usize,
pub max_old_space_size_mb: usize,
pub code_range_size_mb: usize,
pub incremental_marking: bool,
pub concurrent_marking: bool,
pub concurrent_sweeping: bool,
}
impl Default for V8HeapConfig {
fn default() -> Self {
Self {
initial_heap_size_mb: 64,
max_heap_size_mb: 256,
initial_old_space_size_mb: 32,
max_old_space_size_mb: 128,
code_range_size_mb: 32,
incremental_marking: true,
concurrent_marking: true,
concurrent_sweeping: true,
}
}
}
impl V8HeapConfig {
pub fn minimal() -> Self {
Self {
initial_heap_size_mb: 4,
max_heap_size_mb: 16,
initial_old_space_size_mb: 2,
max_old_space_size_mb: 8,
code_range_size_mb: 4,
incremental_marking: false, concurrent_marking: false,
concurrent_sweeping: false,
}
}
pub fn small() -> Self {
Self {
initial_heap_size_mb: 16,
max_heap_size_mb: 64,
initial_old_space_size_mb: 8,
max_old_space_size_mb: 32,
code_range_size_mb: 8,
incremental_marking: true,
concurrent_marking: false,
concurrent_sweeping: true,
}
}
pub fn large() -> Self {
Self {
initial_heap_size_mb: 256,
max_heap_size_mb: 512,
initial_old_space_size_mb: 128,
max_old_space_size_mb: 256,
code_range_size_mb: 64,
incremental_marking: true,
concurrent_marking: true,
concurrent_sweeping: true,
}
}
pub fn maximum() -> Self {
Self {
initial_heap_size_mb: 512,
max_heap_size_mb: 1024,
initial_old_space_size_mb: 256,
max_old_space_size_mb: 512,
code_range_size_mb: 128,
incremental_marking: true,
concurrent_marking: true,
concurrent_sweeping: true,
}
}
}
impl V8HeapPreset {
pub fn config(&self) -> V8HeapConfig {
match self {
V8HeapPreset::Minimal => V8HeapConfig::minimal(),
V8HeapPreset::Small => V8HeapConfig::small(),
V8HeapPreset::Default => V8HeapConfig::default(),
V8HeapPreset::Large => V8HeapConfig::large(),
V8HeapPreset::Maximum => V8HeapConfig::maximum(),
V8HeapPreset::Custom(config) => *config,
}
}
pub fn from_code_complexity(code: &str) -> Self {
let len: _ = code.len();
let func_count: _ = code.matches("function").count() + code.matches("=>").count();
let loop_count: _ = code.matches("for").count()
+ code.matches("while").count()
+ code.matches(".map").count()
+ code.matches(".forEach").count();
let complexity_score: _ = len / 100 + func_count * 10 + loop_count * 5;
match complexity_score {
0..=10 => V8HeapPreset::Minimal,
11..=50 => V8HeapPreset::Small,
51..=200 => V8HeapPreset::Default,
201..=500 => V8HeapPreset::Large,
_ => V8HeapPreset::Maximum,
}
}
}
pub struct V8CreateParamsBuilder {
config: V8HeapConfig,
}
impl V8CreateParamsBuilder {
pub fn with_preset(preset: V8HeapPreset) -> Self {
Self {
config: preset.config(),
}
}
pub fn with_config(config: V8HeapConfig) -> Self {
Self { config }
}
pub fn build(self) -> v8::CreateParams {
v8::CreateParams::default()
}
pub fn recommended_flags(&self) -> Vec<String> {
let mut flags = Vec::new();
flags.push(format!(
"--max-old-space-size={}",
self.config.max_old_space_size_mb
));
if self.config.incremental_marking {
flags.push("--incremental-marking".to_string());
}
if self.config.concurrent_marking {
flags.push("--concurrent-marking".to_string());
}
if self.config.concurrent_sweeping {
flags.push("--concurrent-sweeping".to_string());
}
flags
}
}
pub struct V8ConfigManager {
current_preset: V8HeapPreset,
applied: bool,
}
impl V8ConfigManager {
pub fn new() -> Self {
Self {
current_preset: V8HeapPreset::Default,
applied: false,
}
}
pub fn set_preset(&mut self, preset: V8HeapPreset) {
self.current_preset = preset;
}
pub fn current_preset(&self) -> V8HeapPreset {
self.current_preset
}
pub fn current_config(&self) -> V8HeapConfig {
self.current_preset.config()
}
pub fn mark_applied(&mut self) {
self.applied = true;
}
pub fn is_applied(&self) -> bool {
self.applied
}
pub fn auto_select(&mut self, code: &str) {
self.current_preset = V8HeapPreset::from_code_complexity(code);
}
pub fn estimated_memory_usage(&self) -> usize {
let config: _ = self.current_config();
config.initial_heap_size_mb + config.code_range_size_mb
}
pub fn max_memory_usage(&self) -> usize {
let config: _ = self.current_config();
config.max_heap_size_mb + config.code_range_size_mb
}
}
impl Default for V8ConfigManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::{HashMap, BTreeMap};
#[test]
fn test_heap_presets() {
let minimal: _ = V8HeapPreset::Minimal.config();
assert_eq!(minimal.max_heap_size_mb, 16);
let small: _ = V8HeapPreset::Small.config();
assert_eq!(small.max_heap_size_mb, 64);
let default: _ = V8HeapPreset::Default.config();
assert_eq!(default.max_heap_size_mb, 256);
let large: _ = V8HeapPreset::Large.config();
assert_eq!(large.max_heap_size_mb, 512);
let maximum: _ = V8HeapPreset::Maximum.config();
assert_eq!(maximum.max_heap_size_mb, 1024);
}
#[test]
fn test_code_complexity_detection() {
let simple: _ = "1 + 1";
assert_eq!(
V8HeapPreset::from_code_complexity(simple),
V8HeapPreset::Minimal
);
let medium: _ = r#"
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
for (let i: _ = 0; i < 10; i++) { add(i, i); }
"#;
let preset: _ = V8HeapPreset::from_code_complexity(medium);
assert!(
preset == V8HeapPreset::Small
|| preset == V8HeapPreset::Default
|| preset == V8HeapPreset::Minimal
)));
let complex: _ = r#"
class Calculator {
constructor() { this.history = []; }
add(a, b) { return a + b; }
multiply(a, b) { return a * b; }
}
const calc = new Calculator();
const results = [1,2,3,4,5].map(x => calc.add(x, x)).filter(x => x > 5);
for (let i: _ = 0; i < 100; i++) { calc.multiply(i, 2); }
while (calc.history.length > 0) { calc.history.pop(); }
"#;
let preset: _ = V8HeapPreset::from_code_complexity(complex);
assert!(
preset == V8HeapPreset::Small
|| preset == V8HeapPreset::Default
|| preset == V8HeapPreset::Large
)));
}
#[test]
fn test_config_manager() {
let mut manager = V8ConfigManager::new();
assert_eq!(manager.current_preset(), V8HeapPreset::Default);
assert!(!manager.is_applied());
manager.set_preset(V8HeapPreset::Large);
assert_eq!(manager.current_preset(), V8HeapPreset::Large);
manager.mark_applied();
assert!(manager.is_applied());
let memory: _ = manager.estimated_memory_usage();
assert!(memory > 0);
}
#[test]
fn test_create_params_builder() {
let builder: _ = V8CreateParamsBuilder::with_preset(V8HeapPreset::Small);
let flags: _ = builder.recommended_flags();
assert!(flags.iter().any(|f| f.contains("max-old-space-size"));
}
#[test]
fn test_custom_config() {
let custom_config: _ = V8HeapConfig {
initial_heap_size_mb: 100,
max_heap_size_mb: 400,
initial_old_space_size_mb: 50,
max_old_space_size_mb: 200,
code_range_size_mb: 50,
incremental_marking: true,
concurrent_marking: true,
concurrent_sweeping: true,
};
let preset: _ = V8HeapPreset::Custom(custom_config);
let config: _ = preset.config();
assert_eq!(config.max_heap_size_mb, 400);
assert_eq!(config.initial_heap_size_mb, 100);
}
#[test]
fn test_auto_select() {
let mut manager = V8ConfigManager::new();
manager.auto_select("1 + 1");
assert_eq!(manager.current_preset(), V8HeapPreset::Minimal);
manager.auto_select("function complex() { for(;;) {} }".repeat(10).as_str());
assert!(
manager.current_preset() == V8HeapPreset::Default
|| manager.current_preset() == V8HeapPreset::Large
|| manager.current_preset() == V8HeapPreset::Small
)));
}
}