#![forbid(unsafe_code)]
use std::collections::BTreeMap;
use boa_engine::{Context, Source};
mod scaling;
mod shape;
mod sizes;
mod table;
pub use scaling::{
build, code_lines, deepest_fold, linked_runtime_bytes, linked_runtime_bytes_in,
program_with_components, program_with_depth, program_with_roots, program_with_signals,
program_without_components, runtime_js_bytes, survey, template_bytes, time_graph_passes,
Emitted, GraphTimes, FOREIGN_VIEW_PROGRAM, NULL_PROGRAM, SMALLEST_PROGRAM,
SWIFT_BYTES_PER_LINE, SWIFT_LARGEST_APP_JS, SWIFT_LARGEST_APP_LINES, SWIFT_NULL_PROGRAM_JS,
SWIFT_NULL_PROGRAM_LINES,
};
pub use shape::{benchmark_row, emitted_row, RowShape};
pub use sizes::{bundle_sizes, compile, repository_path, try_compile, BundleSize};
pub use table::{generated_section, END_MARKER, START_MARKER};
pub const INSTRUMENT_JS: &str = include_str!("../js/instrument.js");
pub const BENCHMARK_JS: &str = include_str!("../js/benchmark.js");
pub const REORDER_JS: &str = include_str!("../js/reorder.js");
pub use zdc_runtime::DOM_SHIM_JS;
pub const ROW_ZD: &str = include_str!("../bench/row.zd");
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Measurement {
pub arm: String,
pub step: String,
pub fields: BTreeMap<String, i64>,
}
impl Measurement {
pub fn get(&self, key: &str) -> i64 {
self.fields.get(key).copied().unwrap_or(0)
}
}
pub struct Report(pub Vec<Measurement>);
impl Report {
pub fn find(&self, arm: &str, step: &str) -> &Measurement {
self.0
.iter()
.find(|m| m.arm == arm && m.step == step)
.unwrap_or_else(|| panic!("no measurement for `{arm}` / `{step}`"))
}
pub fn arms(&self) -> Vec<&str> {
let mut out: Vec<&str> = Vec::new();
for measurement in &self.0 {
if !out.contains(&measurement.arm.as_str()) {
out.push(&measurement.arm);
}
}
out
}
pub fn steps(&self) -> Vec<&str> {
let mut out: Vec<&str> = Vec::new();
for measurement in &self.0 {
if !out.contains(&measurement.step.as_str()) {
out.push(&measurement.step);
}
}
out
}
}
fn flatten(source: &str) -> String {
source
.lines()
.filter(|line| !line.trim_start().starts_with("import "))
.map(|line| line.strip_prefix("export ").unwrap_or(line))
.collect::<Vec<_>>()
.join("\n")
}
fn measure(what: &str, script: &str) -> Report {
let mut context = Context::default();
let sources = [
("dom shim", DOM_SHIM_JS.to_string()),
("signal.js", flatten(zdc_runtime::SIGNAL_JS)),
("dom.js", flatten(zdc_runtime::DOM_JS)),
("markup.js", flatten(zdc_runtime::MARKUP_JS)),
("list.js", flatten(zdc_runtime::LIST_JS)),
("elements.js", flatten(zdc_runtime::ELEMENTS_JS)),
("instrument.js", INSTRUMENT_JS.to_string()),
];
for (name, source) in sources {
context
.eval(Source::from_bytes(source.as_bytes()))
.unwrap_or_else(|e| panic!("{name} failed to evaluate: {e}"));
}
let report = context
.eval(Source::from_bytes(script.as_bytes()))
.unwrap_or_else(|e| panic!("{what} failed: {e}"))
.to_string(&mut context)
.expect("a measuring script returns a string")
.to_std_string_escaped();
Report(parse(&report))
}
pub fn run() -> Report {
measure("the workload", BENCHMARK_JS)
}
pub fn run_reorder() -> Report {
measure("the reorder measurement", REORDER_JS)
}
fn parse(report: &str) -> Vec<Measurement> {
let mut out = Vec::new();
for line in report.lines() {
let Some(rest) = line.strip_prefix("RESULT\t") else {
continue;
};
let mut parts = rest.split('\t');
let arm = parts.next().expect("a result line names its arm");
let step = parts.next().expect("a result line names its step");
let fields = parts.next().expect("a result line carries fields");
let mut counts = BTreeMap::new();
for field in fields.split(',') {
let (key, value) = field
.split_once('=')
.unwrap_or_else(|| panic!("malformed field `{field}`"));
let value: i64 = value
.parse()
.unwrap_or_else(|e| panic!("`{field}` is not a number: {e}"));
counts.insert(key.to_string(), value);
}
out.push(Measurement {
arm: arm.to_string(),
step: step.to_string(),
fields: counts,
});
}
assert!(
!out.is_empty(),
"the workload produced no measurements — it probably did not run"
);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_result_line_parses_into_counters() {
let parsed = parse("RESULT\tzd\tcreate\trows=2,cross.cloneNode=2\nnoise\n");
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].arm, "zd");
assert_eq!(parsed[0].step, "create");
assert_eq!(parsed[0].get("cross.cloneNode"), 2);
assert_eq!(parsed[0].get("absent"), 0);
}
#[test]
fn flatten_strips_module_syntax_and_nothing_else() {
let flattened = flatten("import { a } from './b.js';\nexport function c() {}\n indented");
assert_eq!(flattened, "function c() {}\n indented");
}
}