use crate::parser::Statement;
pub struct WebWorkerGenerator {
worker_count: usize,
}
impl WebWorkerGenerator {
pub fn new() -> Self {
Self { worker_count: 0 }
}
pub fn contains_spawn(stmt: &Statement) -> bool {
match stmt {
Statement::Thread { .. } | Statement::Async { .. } => true,
Statement::If {
then_block,
else_block,
..
} => {
then_block.iter().any(|s| Self::contains_spawn(s))
|| else_block
.as_ref()
.is_some_and(|b| b.iter().any(|s| Self::contains_spawn(s)))
}
_ => false,
}
}
pub fn generate_worker<'ast>(&mut self, _body: &[&'ast Statement<'ast>]) -> String {
self.worker_count += 1;
let worker_id = self.worker_count;
let mut output = String::new();
output.push_str(&format!(
"// Worker {}\nconst worker{} = new Worker(",
worker_id, worker_id
));
output.push_str("URL.createObjectURL(new Blob([`\n");
output.push_str("self.onmessage = function(e) {\n");
output.push_str(" try {\n");
output.push_str(" // Worker code\n");
output.push_str(" const result = (function() {\n");
output.push_str(" // Execute spawned code\n");
output.push_str(" })();\n");
output.push_str(" self.postMessage({ success: true, result });\n");
output.push_str(" } catch (error) {\n");
output.push_str(" self.postMessage({ success: false, error: error.message });\n");
output.push_str(" }\n");
output.push_str("};\n");
output.push_str("`], { type: 'application/javascript' })));\n\n");
output.push_str(&format!(
"worker{}.postMessage({{}}); // Start worker\n",
worker_id
));
output
}
pub fn generate_helpers() -> String {
r#"
// Web Worker Helper Functions
function createWorkerFromFunction(fn) {
const blob = new Blob([`
self.onmessage = function(e) {
const fn = ${fn.toString()};
try {
const result = fn(e.data);
self.postMessage({ success: true, result });
} catch (error) {
self.postMessage({ success: false, error: error.message });
}
};
`], { type: 'application/javascript' });
return new Worker(URL.createObjectURL(blob));
}
function spawnWorker(fn, data) {
return new Promise((resolve, reject) => {
const worker = createWorkerFromFunction(fn);
worker.onmessage = function(e) {
worker.terminate();
if (e.data.success) {
resolve(e.data.result);
} else {
reject(new Error(e.data.error));
}
};
worker.onerror = function(error) {
worker.terminate();
reject(error);
};
worker.postMessage(data);
});
}
// Channel simulation using SharedArrayBuffer (if available) or MessageChannel
class Channel {
constructor() {
if (typeof SharedArrayBuffer !== 'undefined') {
// Use SharedArrayBuffer for true shared memory
this.buffer = new SharedArrayBuffer(1024);
this.view = new Int32Array(this.buffer);
this.writeIndex = 0;
this.readIndex = 0;
} else {
// Fallback to message passing
this.messageChannel = new MessageChannel();
this.queue = [];
}
}
send(value) {
if (this.messageChannel) {
this.messageChannel.port1.postMessage(value);
} else {
// SharedArrayBuffer implementation
const json = JSON.stringify(value);
// Write to shared memory (simplified)
}
}
async recv() {
if (this.messageChannel) {
return new Promise((resolve) => {
this.messageChannel.port2.onmessage = (e) => {
resolve(e.data);
};
});
} else {
// SharedArrayBuffer implementation
return new Promise((resolve) => {
// Read from shared memory (simplified)
});
}
}
}
function createChannel() {
return new Channel();
}
"#
.to_string()
}
}
impl Default for WebWorkerGenerator {
fn default() -> Self {
Self::new()
}
}
pub fn transform_spawn_to_workers(statements: &[Statement]) -> String {
let mut generator = WebWorkerGenerator::new();
let mut output = String::new();
output.push_str(&WebWorkerGenerator::generate_helpers());
output.push('\n');
for stmt in statements {
match stmt {
Statement::Thread { body, .. } | Statement::Async { body, .. } => {
output.push_str(&generator.generate_worker(body));
}
_ => {}
}
}
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::Expression;
use crate::test_utils::test_alloc_expr;
#[test]
fn test_generate_helpers() {
let helpers = WebWorkerGenerator::generate_helpers();
assert!(helpers.contains("createWorkerFromFunction"));
assert!(helpers.contains("spawnWorker"));
assert!(helpers.contains("Channel"));
}
#[test]
fn test_web_worker_generator() {
let mut generator = WebWorkerGenerator::new();
let worker_code = generator.generate_worker(&[]);
assert!(worker_code.contains("new Worker"));
assert!(worker_code.contains("Blob"));
assert!(worker_code.contains("onmessage"));
}
#[test]
fn test_contains_spawn() {
let thread_stmt = Statement::Thread {
body: vec![],
location: None,
};
let async_stmt = Statement::Async {
body: vec![],
location: None,
};
assert!(WebWorkerGenerator::contains_spawn(&thread_stmt));
assert!(WebWorkerGenerator::contains_spawn(&async_stmt));
let regular_stmt = Statement::Expression {
expr: test_alloc_expr(Expression::Literal {
value: crate::parser::Literal::Int(42),
location: None,
}),
location: None,
};
assert!(!WebWorkerGenerator::contains_spawn(®ular_stmt));
}
}