diff --git a/.gitignore b/.gitignore index 42c96f2e8c5..972d1eb0f84 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,9 @@ yarn-error.log # tests/js/test.js is used for testing changes locally tests/js/test.js + +# Profiling +*.string_data +*.string_index +*.events +chrome_profiler.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 49d85082624..aec1c2cd7b1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -20,6 +20,24 @@ "clear": true } }, + { + "type": "process", + "label": "Cargo Run (Profiler)", + "command": "cargo", + "args": ["run", "--features", "Boa/profiler", "../tests/js/test.js"], + "problemMatcher": ["$rustc"], + "group": { + "kind": "build", + "isDefault": true + }, + "options": { + "env": { "RUST_BACKTRACE": "full" }, + "cwd": "${workspaceFolder}/boa_cli" + }, + "presentation": { + "clear": true + } + }, { "type": "process", "label": "Get Tokens", diff --git a/Cargo.lock b/Cargo.lock index 3bc70dc1544..486605160d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,8 +8,10 @@ dependencies = [ "criterion", "gc", "jemallocator", + "measureme", "num-bigint", "num-traits", + "once_cell", "rand", "regex", "rustc-hash", @@ -136,6 +138,15 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + [[package]] name = "criterion" version = "0.3.2" @@ -359,6 +370,15 @@ version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.8" @@ -374,12 +394,34 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +[[package]] +name = "measureme" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef709d3257013bba7cff14fc504e07e80631d3fe0f6d38ce63b8f6510ccb932" +dependencies = [ + "byteorder", + "memmap", + "parking_lot", + "rustc-hash", +] + [[package]] name = "memchr" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "memoffset" version = "0.5.4" @@ -430,12 +472,44 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + [[package]] name = "oorandom" version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api", + "parking_lot_core", + "rustc_version", +] + +[[package]] +name = "parking_lot_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "rustc_version", + "smallvec", + "winapi", +] + [[package]] name = "plotters" version = "0.2.14" @@ -563,6 +637,12 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + [[package]] name = "regex" version = "1.3.7" @@ -672,6 +752,15 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "strsim" version = "0.8.0" diff --git a/README.md b/README.md index 86b5db41497..44089762643 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,10 @@ See [CHANGELOG.md](./CHANGELOG.md). - Run with `cargo run -- test.js` where `test.js` is an existing JS file. - If any JS doesn't work then it's a bug. Please raise an issue! +## Profiling + +See [Profiling](./docs/profiling.md) + ## Command-line Options ``` diff --git a/boa/Cargo.toml b/boa/Cargo.toml index aebe579223f..a92820c5585 100644 --- a/boa/Cargo.toml +++ b/boa/Cargo.toml @@ -10,6 +10,9 @@ license = "Unlicense/MIT" exclude = ["../.vscode/*", "../Dockerfile", "../Makefile", "../.editorConfig"] edition = "2018" +[features] +profiler = ["measureme", "once_cell"] + [dependencies] gc = { version = "0.3.5", features = ["derive"] } serde_json = "1.0.53" @@ -18,10 +21,12 @@ num-traits = "0.2.11" regex = "1.3.7" rustc-hash = "1.1.0" num-bigint = { version = "0.2.6", features = ["serde"] } +bitflags = "1.2.1" # Optional Dependencies serde = { version = "1.0.110", features = ["derive"], optional = true } -bitflags = "1.2.1" +measureme = { version = "0.7.1", optional = true } +once_cell = { version = "1.4.0", optional = true } [dev-dependencies] criterion = "0.3.2" diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 4cf03d37901..9fa9ca73ee8 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -21,6 +21,7 @@ use crate::{ value::{same_value_zero, ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use std::{ borrow::Borrow, @@ -1042,6 +1043,7 @@ impl Array { /// Initialise the `Array` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("array", "init"); global.set_field("Array", Self::create(global)); } } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index f01b4573c6a..6e4ac8f5310 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -20,6 +20,7 @@ use crate::{ }, exec::Interpreter, syntax::ast::bigint::BigInt as AstBigInt, + BoaProfiler, }; #[cfg(test)] @@ -130,6 +131,7 @@ impl BigInt { /// Initialise the `BigInt` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("bigint", "init"); global.set_field("BigInt", Self::create(global)); } } diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index 40b7de056dd..76012a40e7d 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -19,6 +19,7 @@ use crate::{ value::{ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use std::{borrow::Borrow, ops::Deref}; @@ -128,6 +129,7 @@ impl Boolean { /// Initialise the `Boolean` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("boolean", "init"); global.set_field("Boolean", Self::create(global)); } } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index b181e217458..2d678217ac7 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -23,6 +23,7 @@ use crate::{ value::{display_obj, ResultValue, Value}, }, exec::Interpreter, + BoaProfiler, }; use rustc_hash::FxHashMap; use std::time::SystemTime; @@ -520,5 +521,6 @@ pub fn create(global: &Value) -> Value { /// Initialise the `console` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("console", "init"); global.set_field("console", create(global)); } diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 328edc50bfb..32eda95b3af 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -17,6 +17,7 @@ use crate::{ value::{ResultValue, Value}, }, exec::Interpreter, + profiler::BoaProfiler, }; // mod eval; @@ -80,6 +81,7 @@ impl Error { /// Initialise the global object with the `Error` object. pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("error", "init"); global.set_field("Error", Self::create(global)); } } diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 34e4c88ff77..ce0c4552b90 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -17,6 +17,7 @@ use crate::{ value::{ResultValue, Value}, }, exec::Interpreter, + profiler::BoaProfiler, }; /// JavaScript `RangeError` impleentation. @@ -91,6 +92,7 @@ impl RangeError { /// Initialise the global object with the `RangeError` object. pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("rangeerror", "init"); global.set_field("RangeError", Self::create(global)); } } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 2886b97051f..f719bd064fd 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -22,6 +22,7 @@ use crate::{ environment::lexical_environment::{new_function_environment, Environment}, exec::{Executable, Interpreter}, syntax::ast::node::{FormalParameter, StatementList}, + BoaProfiler, }; use gc::{unsafe_empty_trace, Finalize, Trace}; use std::fmt::{self, Debug}; @@ -142,6 +143,7 @@ impl Function { where P: Into>, { + let _timer = BoaProfiler::global().start_event("function::builtin", "function"); Self::new( parameter_list.into(), None, @@ -163,6 +165,7 @@ impl Function { interpreter: &mut Interpreter, this_obj: &mut Value, ) -> ResultValue { + let _timer = BoaProfiler::global().start_event("function::call", "function"); if self.callable { match self.body { FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter), @@ -433,6 +436,9 @@ pub fn make_builtin_fn(function: NativeFunctionData, name: N, parent: &Value, where N: Into, { + let name_copy: String = name.into(); + let label = format!("{}{}", String::from("make_builtin_fn: "), &name_copy); + let _timer = BoaProfiler::global().start_event(&label, "init"); let func = Function::builtin(Vec::new(), function); let mut new_func = Object::function(); @@ -441,11 +447,12 @@ where let new_func_obj = Value::from(new_func); new_func_obj.set_field("length", length); - parent.set_field(name.into(), new_func_obj); + parent.set_field(Value::from(name_copy), new_func_obj); } /// Initialise the `Function` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("function", "init"); global.set_field("Function", create(global)); } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index ad7cd378fd0..673e9f1b85c 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -19,7 +19,7 @@ use crate::builtins::{ property::Property, value::{ResultValue, Value}, }; -use crate::exec::Interpreter; +use crate::{exec::Interpreter, BoaProfiler}; use serde_json::{self, Value as JSONValue}; #[cfg(test)] @@ -142,5 +142,6 @@ pub fn create(global: &Value) -> Value { /// Initialise the `JSON` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("json", "init"); global.set_field("JSON", create(global)); } diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 04591cad309..775642ae299 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -17,6 +17,7 @@ use crate::{ value::{ResultValue, Value}, }, exec::Interpreter, + BoaProfiler, }; use rand::random; use std::f64; @@ -507,6 +508,7 @@ pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// Create a new `Math` object pub fn create(global: &Value) -> Value { + let _timer = BoaProfiler::global().start_event("math:create", "init"); let math = Value::new_object(Some(global)); math.set_field("E", Value::from(f64::consts::E)); @@ -553,5 +555,6 @@ pub fn create(global: &Value) -> Value { /// Initialise the `Math` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("math", "init"); global.set_field("Math", create(global)); } diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 05cde1dcf13..99f8b38cddc 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -27,6 +27,7 @@ use crate::{ RangeError, }, exec::Interpreter, + BoaProfiler, }; use num_traits::float::FloatCore; use std::{borrow::Borrow, f64, ops::Deref}; @@ -421,6 +422,7 @@ impl Number { /// Initialise the `Number` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("number", "init"); global.set_field("Number", Self::create(global)); } diff --git a/boa/src/builtins/object/internal_methods_trait.rs b/boa/src/builtins/object/internal_methods_trait.rs index d99531bed85..47b21dd2827 100644 --- a/boa/src/builtins/object/internal_methods_trait.rs +++ b/boa/src/builtins/object/internal_methods_trait.rs @@ -5,10 +5,13 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots -use crate::builtins::{ - object::{Object, INSTANCE_PROTOTYPE}, - property::Property, - value::{same_value, Value, ValueData}, +use crate::{ + builtins::{ + object::{Object, INSTANCE_PROTOTYPE}, + property::Property, + value::{same_value, Value, ValueData}, + }, + BoaProfiler, }; use std::borrow::Borrow; use std::ops::Deref; @@ -131,6 +134,7 @@ pub trait ObjectInternalMethods { /// [[Set]] /// fn set(&mut self, field: Value, val: Value) -> bool { + let _timer = BoaProfiler::global().start_event("Object::set", "object"); // [1] debug_assert!(Property::is_property_key(&field)); @@ -168,6 +172,7 @@ pub trait ObjectInternalMethods { } fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { + let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object"); let mut current = self.get_own_property(&Value::from(property_key.to_string())); let extensible = self.is_extensible(); diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 912d15203cd..a366fe5c034 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -20,6 +20,7 @@ use crate::{ value::{same_value, ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use gc::{unsafe_empty_trace, Finalize, Trace}; use rustc_hash::FxHashMap; @@ -128,6 +129,7 @@ impl ObjectInternalMethods for Object { /// Helper function to get an immutable internal slot or Null fn get_internal_slot(&self, name: &str) -> Value { + let _timer = BoaProfiler::global().start_event("Object::get_internal_slot", "object"); match self.internal_slots.get(name) { Some(v) => v.clone(), None => Value::null(), @@ -143,6 +145,7 @@ impl ObjectInternalMethods for Object { /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p fn get_own_property(&self, prop: &Value) -> Property { + let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object"); debug_assert!(Property::is_property_key(prop)); // Prop could either be a String or Symbol match *(*prop) { @@ -349,6 +352,7 @@ impl Object { /// Return a new ObjectData struct, with `kind` set to Ordinary pub fn function() -> Self { + let _timer = BoaProfiler::global().start_event("Object::Function", "object"); let mut object = Self { kind: ObjectKind::Function, internal_slots: FxHashMap::default(), @@ -633,5 +637,6 @@ pub fn create(global: &Value) -> Value { /// Initialise the `Object` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("object", "init"); global.set_field("Object", create(global)); } diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index af82d3bfe80..64c88eda8a4 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -21,6 +21,7 @@ use crate::{ value::{ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; #[cfg(test)] @@ -491,6 +492,7 @@ impl RegExp { /// Initialise the `RegExp` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("regexp", "init"); global.set_field("RegExp", Self::create(global)); } } diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 06ba55460b1..4197e3dfda5 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -21,6 +21,7 @@ use crate::{ RegExp, }, exec::Interpreter, + BoaProfiler, }; use regex::Regex; use std::string::String as StdString; @@ -1077,6 +1078,7 @@ impl String { /// Initialise the `String` object on the global object. #[inline] pub(crate) fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("string", "init"); global.set_field("String", Self::create(global)); } } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index e71a5dc09c3..bbf915bfb44 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -28,6 +28,7 @@ use crate::{ value::{ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use gc::{Gc, GcCell}; use rand::random; @@ -100,5 +101,6 @@ pub fn create(global: &Value) -> Value { /// Initialise the `Symbol` object on the global object. #[inline] pub fn init(global: &Value) { + let _timer = BoaProfiler::global().start_event("symbol", "init"); global.set_field("Symbol", create(global)); } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index b2459cf40f0..be23230289a 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -9,6 +9,7 @@ impl From<&Value> for Value { impl From for Value { fn from(value: String) -> Self { + let _timer = BoaProfiler::global().start_event("From", "value"); Self::string(value) } } @@ -170,6 +171,7 @@ where impl From for Value { fn from(object: Object) -> Self { + let _timer = BoaProfiler::global().start_event("From", "value"); Value::object(object) } } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index ec682c7b09e..807b4fe9b27 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -13,7 +13,7 @@ use crate::builtins::{ }, property::Property, }; -use crate::syntax::ast::bigint::BigInt; +use crate::{syntax::ast::bigint::BigInt, BoaProfiler}; use gc::{Finalize, Gc, GcCell, GcCellRef, Trace}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use std::{ @@ -122,6 +122,7 @@ impl Value { /// Returns a new empty object pub fn new_object(global: Option<&Value>) -> Self { + let _timer = BoaProfiler::global().start_event("new_object", "value"); if let Some(global) = global { let object_prototype = global.get_field("Object").get_field(PROTOTYPE); @@ -397,6 +398,7 @@ impl ValueData { /// /// A copy of the Property is returned. pub fn get_property(&self, field: &str) -> Option { + let _timer = BoaProfiler::global().start_event("Value::get_property", "value"); // Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154 // This is only for primitive strings, String() objects have their lengths calculated in string.rs if self.is_string() && field == "length" { @@ -443,6 +445,7 @@ impl ValueData { writable: Option, configurable: Option, ) { + let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); let obj: Option = match self { Self::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()), _ => None, @@ -463,6 +466,7 @@ impl ValueData { /// /// Returns a copy of the Property. pub fn get_internal_slot(&self, field: &str) -> Value { + let _timer = BoaProfiler::global().start_event("Value::get_internal_slot", "value"); let obj: Object = match *self { Self::Object(ref obj) => { let hash = obj.clone(); @@ -488,6 +492,7 @@ impl ValueData { where F: Into, { + let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); match *field.into() { // Our field will either be a String or a Symbol Self::String(ref s) => { @@ -586,6 +591,7 @@ impl ValueData { /// Check to see if the Value has the field, mainly used by environment records pub fn has_field(&self, field: &str) -> bool { + let _timer = BoaProfiler::global().start_event("Value::has_field", "value"); self.get_property(field).is_some() } @@ -596,6 +602,7 @@ impl ValueData { F: Into, V: Into, { + let _timer = BoaProfiler::global().start_event("Value::set_field", "value"); let field = field.into(); let val = val.into(); @@ -625,6 +632,7 @@ impl ValueData { /// Set the private field in the value pub fn set_internal_slot(&self, field: &str, val: Value) -> Value { + let _timer = BoaProfiler::global().start_event("Value::set_internal_slot", "exec"); if let Self::Object(ref obj) = *self { obj.borrow_mut() .internal_slots @@ -764,6 +772,7 @@ impl ValueData { /// /// https://tc39.es/ecma262/#sec-typeof-operator pub fn get_type(&self) -> &'static str { + let _timer = BoaProfiler::global().start_event("Value::get_type", "value"); match *self { Self::Rational(_) | Self::Integer(_) => "number", Self::String(_) => "string", diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 65919b446ec..8e32c099457 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -14,6 +14,7 @@ use crate::{ global_environment_record::GlobalEnvironmentRecord, object_environment_record::ObjectEnvironmentRecord, }, + BoaProfiler, }; use gc::{Gc, GcCell}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -79,6 +80,7 @@ impl error::Error for EnvironmentError { impl LexicalEnvironment { pub fn new(global: Value) -> Self { + let _timer = BoaProfiler::global().start_event("LexicalEnvironment::new", "env"); let global_env = new_global_environment(global.clone(), global); let mut lexical_env = Self { environment_stack: VecDeque::new(), @@ -219,6 +221,7 @@ impl LexicalEnvironment { } pub fn new_declarative_environment(env: Option) -> Environment { + let _timer = BoaProfiler::global().start_event("new_declarative_environment", "env"); let boxed_env = Box::new(DeclarativeEnvironmentRecord { env_rec: FxHashMap::default(), outer_env: env, diff --git a/boa/src/exec/array/mod.rs b/boa/src/exec/array/mod.rs index 548acd461f3..1f0374f9bb9 100644 --- a/boa/src/exec/array/mod.rs +++ b/boa/src/exec/array/mod.rs @@ -4,10 +4,12 @@ use super::{Executable, Interpreter}; use crate::{ builtins::{Array, ResultValue}, syntax::ast::node::{ArrayDecl, Node}, + BoaProfiler, }; impl Executable for ArrayDecl { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec"); let array = Array::new_array(interpreter)?; let mut elements = Vec::new(); for elem in self.as_ref() { diff --git a/boa/src/exec/block/mod.rs b/boa/src/exec/block/mod.rs index c592a05ab92..deec6e432bc 100644 --- a/boa/src/exec/block/mod.rs +++ b/boa/src/exec/block/mod.rs @@ -5,10 +5,12 @@ use crate::{ builtins::value::{ResultValue, Value}, environment::lexical_environment::new_declarative_environment, syntax::ast::node::Block, + BoaProfiler, }; impl Executable for Block { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("Block", "exec"); { let env = &mut interpreter.realm_mut().environment; env.push(new_declarative_environment(Some( diff --git a/boa/src/exec/declaration/mod.rs b/boa/src/exec/declaration/mod.rs index 0ab755872ef..192a8a168fa 100644 --- a/boa/src/exec/declaration/mod.rs +++ b/boa/src/exec/declaration/mod.rs @@ -10,10 +10,12 @@ use crate::{ syntax::ast::node::{ ArrowFunctionDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDeclList, VarDeclList, }, + BoaProfiler, }; impl Executable for FunctionDecl { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec"); let val = interpreter.create_function( self.parameters().to_vec(), self.body().to_vec(), diff --git a/boa/src/exec/expression/mod.rs b/boa/src/exec/expression/mod.rs index 57fc661ca7a..7087dfe14e0 100644 --- a/boa/src/exec/expression/mod.rs +++ b/boa/src/exec/expression/mod.rs @@ -7,10 +7,12 @@ use crate::{ value::{ResultValue, Value, ValueData}, }, syntax::ast::node::{Call, New, Node}, + BoaProfiler, }; impl Executable for Call { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("Call", "exec"); let (mut this, func) = match self.expr() { Node::GetConstField(ref obj, ref field) => { let mut obj = obj.run(interpreter)?; diff --git a/boa/src/exec/iteration/mod.rs b/boa/src/exec/iteration/mod.rs index ab1f8308d2f..dd4f728629e 100644 --- a/boa/src/exec/iteration/mod.rs +++ b/boa/src/exec/iteration/mod.rs @@ -5,11 +5,13 @@ use crate::{ builtins::value::{ResultValue, Value}, environment::lexical_environment::new_declarative_environment, syntax::ast::node::ForLoop, + BoaProfiler, }; impl Executable for ForLoop { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { // Create the block environment. + let _timer = BoaProfiler::global().start_event("ForLoop", "exec"); { let env = &mut interpreter.realm_mut().environment; env.push(new_declarative_environment(Some( diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 4f0c8266818..d92e2a8f2e9 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,6 +26,7 @@ use crate::{ constant::Const, node::{FormalParameter, MethodDefinitionKind, Node, PropertyDefinition, StatementList}, }, + BoaProfiler, }; use std::{borrow::Borrow, ops::Deref}; @@ -373,6 +374,7 @@ impl Interpreter { impl Executable for Node { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { Node::Const(Const::Null) => Ok(Value::null()), Node::Const(Const::Undefined) => Ok(Value::undefined()), diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index d4cb97495cb..29cef4bb504 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -8,11 +8,13 @@ use crate::{ node::{Assign, BinOp, Node, UnaryOp}, op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp}, }, + BoaProfiler, }; use std::borrow::BorrowMut; impl Executable for Assign { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("Assign", "exec"); let val = self.rhs().run(interpreter)?; match self.lhs() { Node::Identifier(ref name) => { diff --git a/boa/src/exec/statement_list.rs b/boa/src/exec/statement_list.rs index 31558e383f1..4863232171e 100644 --- a/boa/src/exec/statement_list.rs +++ b/boa/src/exec/statement_list.rs @@ -4,10 +4,12 @@ use super::{Executable, Interpreter}; use crate::{ builtins::value::{ResultValue, Value}, syntax::ast::node::StatementList, + BoaProfiler, }; impl Executable for StatementList { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("StatementList", "exec"); let mut obj = Value::null(); for (i, item) in self.statements().iter().enumerate() { let val = item.run(interpreter)?; diff --git a/boa/src/exec/try_node/mod.rs b/boa/src/exec/try_node/mod.rs index 8b45e46e1a8..cb6b25a43f6 100644 --- a/boa/src/exec/try_node/mod.rs +++ b/boa/src/exec/try_node/mod.rs @@ -5,6 +5,7 @@ use crate::{ builtins::value::ResultValue, environment::lexical_environment::{new_declarative_environment, VariableScope}, syntax::ast::node::Try, + BoaProfiler, }; #[cfg(test)] @@ -12,6 +13,7 @@ mod tests; impl Executable for Try { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { + let _timer = BoaProfiler::global().start_event("Try", "exec"); let res = self.block().run(interpreter).map_or_else( |err| { if let Some(catch) = self.catch() { diff --git a/boa/src/lib.rs b/boa/src/lib.rs index ed75621262d..4314d77f794 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -30,18 +30,21 @@ clippy::must_use_candidate, clippy::missing_errors_doc, clippy::as_conversions, + clippy::let_unit_value, missing_doc_code_examples )] pub mod builtins; pub mod environment; pub mod exec; +pub mod profiler; pub mod realm; pub mod syntax; use crate::{builtins::value::ResultValue, syntax::ast::node::StatementList}; pub use crate::{ exec::{Executable, Interpreter}, + profiler::BoaProfiler, realm::Realm, syntax::{lexer::Lexer, parser::Parser}, }; @@ -71,15 +74,23 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String { /// The str is consumed and the state of the Interpreter is changed /// Similar to `forward`, except the current value is returned instad of the string /// If the interpreter fails parsing an error value is returned instead (error object) +#[allow(clippy::unit_arg, clippy::drop_copy)] pub fn forward_val(engine: &mut Interpreter, src: &str) -> ResultValue { + let main_timer = BoaProfiler::global().start_event("Main", "Main"); // Setup executor - match parser_expr(src) { + let result = match parser_expr(src) { Ok(expr) => expr.run(engine), Err(e) => { eprintln!("{}", e); std::process::exit(1); } - } + }; + + // The main_timer needs to be dropped before the BoaProfiler is. + drop(main_timer); + BoaProfiler::global().drop(); + + result } /// Create a clean Interpreter and execute the code diff --git a/boa/src/profiler.rs b/boa/src/profiler.rs new file mode 100644 index 00000000000..43c7ccbf1f2 --- /dev/null +++ b/boa/src/profiler.rs @@ -0,0 +1,95 @@ +#![allow(missing_copy_implementations, missing_debug_implementations)] + +#[cfg(feature = "profiler")] +use measureme::{EventId, Profiler, TimingGuard}; +#[cfg(feature = "profiler")] +use once_cell::sync::OnceCell; +use std::fmt::{self, Debug}; +#[cfg(feature = "profiler")] +use std::{ + path::Path, + thread::{current, ThreadId}, +}; + +/// MmapSerializatioSink is faster on macOS and Linux +/// but FileSerializationSink is faster on Windows +#[cfg(not(windows))] +#[cfg(feature = "profiler")] +type SerializationSink = measureme::MmapSerializationSink; +#[cfg(windows)] +#[cfg(feature = "profiler")] +type SerializationSink = measureme::FileSerializationSink; +#[cfg(feature = "profiler")] +pub struct BoaProfiler { + profiler: Profiler, +} + +/// This static instance should never be public, and its only access should be done through the `global()` and `drop()` methods +/// This is because `get_or_init` manages synchronisation and the case of an empty value +#[cfg(feature = "profiler")] +static mut INSTANCE: OnceCell = OnceCell::new(); + +#[cfg(feature = "profiler")] +impl BoaProfiler { + pub fn start_event(&self, label: &str, category: &str) -> TimingGuard<'_, SerializationSink> { + let kind = self.profiler.alloc_string(category); + let id = EventId::from_label(self.profiler.alloc_string(label)); + let thread_id = Self::thread_id_to_u32(current().id()); + self.profiler + .start_recording_interval_event(kind, id, thread_id) + } + + pub fn default() -> BoaProfiler { + let profiler = Profiler::new(Path::new("./my_trace")).unwrap(); + BoaProfiler { profiler } + } + + pub fn global() -> &'static BoaProfiler { + unsafe { INSTANCE.get_or_init(Self::default) } + } + + pub fn drop(&self) { + // In order to drop the INSTANCE we need to get ownership of it, which isn't possible on a static unless you make it a mutable static + // mutating statics is unsafe, so we need to wrap it as so. + // This is actually safe though because init and drop are only called at the beginning and end of the application + unsafe { + INSTANCE + .take() + .expect("Could not take back profiler instance"); + } + } + + // Sadly we need to use the unsafe method until this is resolved: + // https://github.com/rust-lang/rust/issues/67939 + // Once `as_64()` is in stable we can do this: + // https://github.com/rust-lang/rust/pull/68531/commits/ea42b1c5b85f649728e3a3b334489bac6dce890a + // Until then our options are: use rust-nightly or use unsafe {} + fn thread_id_to_u32(tid: ThreadId) -> u32 { + unsafe { std::mem::transmute::(tid) as u32 } + } +} + +impl Debug for BoaProfiler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt("no debug implemented", f) + } +} + +#[cfg(not(feature = "profiler"))] +pub struct BoaProfiler; + +#[allow(clippy::unused_unit)] +#[cfg(not(feature = "profiler"))] +impl BoaProfiler { + pub fn start_event(&self, _label: &str, _category: &str) -> () { + () + } + + pub fn drop(&self) { + () + } + + pub fn global() -> BoaProfiler { + BoaProfiler + } +} diff --git a/boa/src/realm.rs b/boa/src/realm.rs index 44c9ac952ac..b6cfc4decf3 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -16,6 +16,7 @@ use crate::{ lexical_environment::LexicalEnvironment, object_environment_record::ObjectEnvironmentRecord, }, + BoaProfiler, }; use gc::{Gc, GcCell}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -32,6 +33,7 @@ pub struct Realm { impl Realm { pub fn create() -> Self { + let _timer = BoaProfiler::global().start_event("Realm::create", "realm"); // Create brand new global object // Global has no prototype to pass None to new_obj let global = Value::new_object(None); @@ -53,6 +55,7 @@ impl Realm { // Sets up the default global objects within Global fn create_instrinsics(&self) { + let _timer = BoaProfiler::global().start_event("create_instrinsics", "realm"); let global = &self.global_obj; // Create intrinsics, add global objects here builtins::init(global); diff --git a/boa/src/syntax/lexer/mod.rs b/boa/src/syntax/lexer/mod.rs index 25431ed4819..3d370d40298 100644 --- a/boa/src/syntax/lexer/mod.rs +++ b/boa/src/syntax/lexer/mod.rs @@ -7,9 +7,12 @@ mod tests; use crate::syntax::ast::bigint::BigInt; -use crate::syntax::ast::{ - token::{NumericLiteral, Token, TokenKind}, - Position, Punctuator, Span, +use crate::{ + syntax::ast::{ + token::{NumericLiteral, Token, TokenKind}, + Position, Punctuator, Span, + }, + BoaProfiler, }; use std::{ char::{decode_utf16, from_u32}, @@ -486,6 +489,7 @@ impl<'a> Lexer<'a> { /// } /// ``` pub fn lex(&mut self) -> Result<(), LexerError> { + let _timer = BoaProfiler::global().start_event("lex", "lexing"); loop { // Check if we've reached the end if self.preview_next().is_none() { diff --git a/boa/src/syntax/parser/expression/assignment/arrow_function.rs b/boa/src/syntax/parser/expression/assignment/arrow_function.rs index ccab9a104bc..608817cf00a 100644 --- a/boa/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa/src/syntax/parser/expression/assignment/arrow_function.rs @@ -8,17 +8,20 @@ //! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions use super::AssignmentExpression; -use crate::syntax::{ - ast::{ - node::{ArrowFunctionDecl, FormalParameter, Node, StatementList}, - Punctuator, TokenKind, - }, - parser::{ - error::{ErrorContext, ParseError, ParseResult}, - function::{FormalParameters, FunctionBody}, - statement::BindingIdentifier, - AllowAwait, AllowIn, AllowYield, Cursor, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{ArrowFunctionDecl, FormalParameter, Node, StatementList}, + Punctuator, TokenKind, + }, + parser::{ + error::{ErrorContext, ParseError, ParseResult}, + function::{FormalParameters, FunctionBody}, + statement::BindingIdentifier, + AllowAwait, AllowIn, AllowYield, Cursor, TokenParser, + }, }, + BoaProfiler, }; /// Arrow function parsing. @@ -60,6 +63,7 @@ impl TokenParser for ArrowFunction { type Output = ArrowFunctionDecl; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("ArrowFunction", "Parsing"); let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind { // CoverParenthesizedExpressionAndArrowParameterList diff --git a/boa/src/syntax/parser/expression/assignment/conditional.rs b/boa/src/syntax/parser/expression/assignment/conditional.rs index 2bea95cc9bb..c2584020170 100644 --- a/boa/src/syntax/parser/expression/assignment/conditional.rs +++ b/boa/src/syntax/parser/expression/assignment/conditional.rs @@ -7,12 +7,15 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator //! [spec]: https://tc39.es/ecma262/#sec-conditional-operator -use crate::syntax::{ - ast::{Node, Punctuator, TokenKind}, - parser::{ - expression::{AssignmentExpression, LogicalORExpression}, - AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{Node, Punctuator, TokenKind}, + parser::{ + expression::{AssignmentExpression, LogicalORExpression}, + AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Conditional expression parsing. @@ -54,6 +57,7 @@ impl TokenParser for ConditionalExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("Conditional", "Parsing"); // TODO: coalesce expression let lhs = LogicalORExpression::new(self.allow_in, self.allow_yield, self.allow_await) .parse(cursor)?; diff --git a/boa/src/syntax/parser/expression/assignment/exponentiation.rs b/boa/src/syntax/parser/expression/assignment/exponentiation.rs index fdc54114a3d..811bde4537d 100644 --- a/boa/src/syntax/parser/expression/assignment/exponentiation.rs +++ b/boa/src/syntax/parser/expression/assignment/exponentiation.rs @@ -7,16 +7,19 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation //! [spec]: https://tc39.es/ecma262/#sec-exp-operator -use crate::syntax::{ - ast::{ - node::{BinOp, Node}, - op::NumOp, - Keyword, Punctuator, TokenKind, - }, - parser::{ - expression::{unary::UnaryExpression, update::UpdateExpression}, - AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{BinOp, Node}, + op::NumOp, + Keyword, Punctuator, TokenKind, + }, + parser::{ + expression::{unary::UnaryExpression, update::UpdateExpression}, + AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Parses an exponentiation expression. @@ -71,6 +74,7 @@ impl TokenParser for ExponentiationExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ExponentiationExpression", "Parsing"); if Self::is_unary_expression(cursor) { return UnaryExpression::new(self.allow_yield, self.allow_await).parse(cursor); } diff --git a/boa/src/syntax/parser/expression/assignment/mod.rs b/boa/src/syntax/parser/expression/assignment/mod.rs index b45f35880db..4f71f217361 100644 --- a/boa/src/syntax/parser/expression/assignment/mod.rs +++ b/boa/src/syntax/parser/expression/assignment/mod.rs @@ -12,12 +12,15 @@ mod conditional; mod exponentiation; use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression}; -use crate::syntax::{ - ast::{ - node::{Assign, BinOp, Node}, - Keyword, Punctuator, TokenKind, +use crate::{ + syntax::{ + ast::{ + node::{Assign, BinOp, Node}, + Keyword, Punctuator, TokenKind, + }, + parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, }, - parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, + BoaProfiler, }; pub(super) use exponentiation::ExponentiationExpression; @@ -70,6 +73,7 @@ impl TokenParser for AssignmentExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("AssignmentExpression", "Parsing"); // Arrow function let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; match next_token.kind { diff --git a/boa/src/syntax/parser/expression/left_hand_side/arguments.rs b/boa/src/syntax/parser/expression/left_hand_side/arguments.rs index a5caf3a9383..4ccdde463bf 100644 --- a/boa/src/syntax/parser/expression/left_hand_side/arguments.rs +++ b/boa/src/syntax/parser/expression/left_hand_side/arguments.rs @@ -7,11 +7,15 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument //! [spec]: https://tc39.es/ecma262/#prod-Arguments -use crate::syntax::{ - ast::{Node, Punctuator, TokenKind}, - parser::{ - expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{Node, Punctuator, TokenKind}, + parser::{ + expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, + TokenParser, + }, }, + BoaProfiler, }; /// Parses a list of arguments. @@ -46,6 +50,7 @@ impl TokenParser for Arguments { type Output = Box<[Node]>; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("Arguments", "Parsing"); cursor.expect(Punctuator::OpenParen, "arguments")?; let mut args = Vec::new(); loop { diff --git a/boa/src/syntax/parser/expression/left_hand_side/call.rs b/boa/src/syntax/parser/expression/left_hand_side/call.rs index 899b1cc5490..8f32a8a8b63 100644 --- a/boa/src/syntax/parser/expression/left_hand_side/call.rs +++ b/boa/src/syntax/parser/expression/left_hand_side/call.rs @@ -8,15 +8,18 @@ //! [spec]: https://tc39.es/ecma262/#prod-CallExpression use super::arguments::Arguments; -use crate::syntax::{ - ast::{ - node::{Call, Node}, - Punctuator, TokenKind, - }, - parser::{ - expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, - TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{Call, Node}, + Punctuator, TokenKind, + }, + parser::{ + expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult, + TokenParser, + }, }, + BoaProfiler, }; /// Parses a call expression. @@ -51,6 +54,7 @@ impl TokenParser for CallExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("CallExpression", "Parsing"); let mut lhs = match cursor.peek(0) { Some(tk) if tk.kind == TokenKind::Punctuator(Punctuator::OpenParen) => { let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor)?; diff --git a/boa/src/syntax/parser/expression/left_hand_side/member.rs b/boa/src/syntax/parser/expression/left_hand_side/member.rs index de13301eaf3..842434740ae 100644 --- a/boa/src/syntax/parser/expression/left_hand_side/member.rs +++ b/boa/src/syntax/parser/expression/left_hand_side/member.rs @@ -6,15 +6,18 @@ //! [spec]: https://tc39.es/ecma262/#prod-MemberExpression use super::arguments::Arguments; -use crate::syntax::{ - ast::{ - node::{Call, New, Node}, - Keyword, Punctuator, TokenKind, - }, - parser::{ - expression::{primary::PrimaryExpression, Expression}, - AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{Call, New, Node}, + Keyword, Punctuator, TokenKind, + }, + parser::{ + expression::{primary::PrimaryExpression, Expression}, + AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Parses a member expression. @@ -47,6 +50,7 @@ impl TokenParser for MemberExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("MemberExpression", "Parsing"); let mut lhs = if cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind == TokenKind::Keyword(Keyword::New) { diff --git a/boa/src/syntax/parser/expression/left_hand_side/mod.rs b/boa/src/syntax/parser/expression/left_hand_side/mod.rs index 737dfdbe85c..1be558b9ec9 100644 --- a/boa/src/syntax/parser/expression/left_hand_side/mod.rs +++ b/boa/src/syntax/parser/expression/left_hand_side/mod.rs @@ -12,9 +12,12 @@ mod call; mod member; use self::{call::CallExpression, member::MemberExpression}; -use crate::syntax::{ - ast::{Node, Punctuator, TokenKind}, - parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Node, Punctuator, TokenKind}, + parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, + }, + BoaProfiler, }; /// Parses a left hand side expression. @@ -49,6 +52,7 @@ impl TokenParser for LeftHandSideExpression { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("LeftHandSIdeExpression", "Parsing"); // TODO: Implement NewExpression: new MemberExpression let lhs = MemberExpression::new(self.allow_yield, self.allow_await).parse(cursor)?; match cursor.peek(0) { diff --git a/boa/src/syntax/parser/expression/mod.rs b/boa/src/syntax/parser/expression/mod.rs index 87317a6df56..0b4137bc150 100644 --- a/boa/src/syntax/parser/expression/mod.rs +++ b/boa/src/syntax/parser/expression/mod.rs @@ -18,9 +18,12 @@ mod update; use self::assignment::ExponentiationExpression; pub(super) use self::{assignment::AssignmentExpression, primary::Initializer}; use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser}; -use crate::syntax::ast::{ - node::{BinOp, Node}, - Keyword, Punctuator, TokenKind, +use crate::{ + profiler::BoaProfiler, + syntax::ast::{ + node::{BinOp, Node}, + Keyword, Punctuator, TokenKind, + }, }; // For use in the expression! macro to allow for both Punctuator and Keyword parameters. @@ -51,6 +54,7 @@ macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $lo type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("Expression", "Parsing"); let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor)?; while let Some(tok) = cursor.peek(0) { match tok.kind { diff --git a/boa/src/syntax/parser/expression/primary/array_initializer/mod.rs b/boa/src/syntax/parser/expression/primary/array_initializer/mod.rs index 999af9a8c17..8bc1e74ace6 100644 --- a/boa/src/syntax/parser/expression/primary/array_initializer/mod.rs +++ b/boa/src/syntax/parser/expression/primary/array_initializer/mod.rs @@ -10,14 +10,18 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{ - node::{ArrayDecl, Node}, - Const, Punctuator, - }, - parser::{ - expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{ArrayDecl, Node}, + Const, Punctuator, + }, + parser::{ + expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, + TokenParser, + }, }, + BoaProfiler, }; /// Parses an array literal. @@ -52,6 +56,7 @@ impl TokenParser for ArrayLiteral { type Output = ArrayDecl; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("ArrayLiteral", "Parsing"); let mut elements = Vec::new(); loop { diff --git a/boa/src/syntax/parser/expression/primary/function_expression.rs b/boa/src/syntax/parser/expression/primary/function_expression.rs index f27bcab26b8..8ebbf7347e4 100644 --- a/boa/src/syntax/parser/expression/primary/function_expression.rs +++ b/boa/src/syntax/parser/expression/primary/function_expression.rs @@ -7,13 +7,16 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function //! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression -use crate::syntax::{ - ast::{node::FunctionExpr, Punctuator}, - parser::{ - function::{FormalParameters, FunctionBody}, - statement::BindingIdentifier, - Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{node::FunctionExpr, Punctuator}, + parser::{ + function::{FormalParameters, FunctionBody}, + statement::BindingIdentifier, + Cursor, ParseError, TokenParser, + }, }, + BoaProfiler, }; /// Function expression parsing. @@ -31,6 +34,7 @@ impl TokenParser for FunctionExpression { type Output = FunctionExpr; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("FunctionExpression", "Parsing"); let name = BindingIdentifier::new(false, false).try_parse(cursor); cursor.expect(Punctuator::OpenParen, "function expression")?; diff --git a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs index f1c6785400f..eaeded52d51 100644 --- a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -10,17 +10,20 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{ - node::{self, FunctionExpr, MethodDefinitionKind, Node}, - token::{Token, TokenKind}, - Punctuator, - }, - parser::{ - expression::AssignmentExpression, - function::{FormalParameters, FunctionBody}, - AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{self, FunctionExpr, MethodDefinitionKind, Node}, + token::{Token, TokenKind}, + Punctuator, + }, + parser::{ + expression::AssignmentExpression, + function::{FormalParameters, FunctionBody}, + AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Parses an object literal. @@ -55,6 +58,7 @@ impl TokenParser for ObjectLiteral { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ObjectLiteral", "Parsing"); let mut elements = Vec::new(); loop { diff --git a/boa/src/syntax/parser/statement/block/mod.rs b/boa/src/syntax/parser/statement/block/mod.rs index 3db21706cb9..843cb0341ea 100644 --- a/boa/src/syntax/parser/statement/block/mod.rs +++ b/boa/src/syntax/parser/statement/block/mod.rs @@ -11,9 +11,12 @@ mod tests; use super::StatementList; -use crate::syntax::{ - ast::{node, Punctuator, TokenKind}, - parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, +use crate::{ + profiler::BoaProfiler, + syntax::{ + ast::{node, Punctuator, TokenKind}, + parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, + }, }; /// A `BlockStatement` is equivalent to a `Block`. @@ -59,6 +62,7 @@ impl TokenParser for Block { type Output = node::Block; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("Block", "Parsing"); cursor.expect(Punctuator::OpenBlock, "block")?; if let Some(tk) = cursor.peek(0) { if tk.kind == TokenKind::Punctuator(Punctuator::CloseBlock) { diff --git a/boa/src/syntax/parser/statement/break_stm/mod.rs b/boa/src/syntax/parser/statement/break_stm/mod.rs index d203097b059..9c7b9e14627 100644 --- a/boa/src/syntax/parser/statement/break_stm/mod.rs +++ b/boa/src/syntax/parser/statement/break_stm/mod.rs @@ -11,9 +11,12 @@ mod tests; use super::LabelIdentifier; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, + }, + BoaProfiler, }; /// Break statement parsing @@ -48,6 +51,7 @@ impl TokenParser for BreakStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("BreakStatement", "Parsing"); cursor.expect(Keyword::Break, "break statement")?; let label = if let (true, tok) = cursor.peek_semicolon(false) { diff --git a/boa/src/syntax/parser/statement/continue_stm/mod.rs b/boa/src/syntax/parser/statement/continue_stm/mod.rs index 7da0b5414cb..8614a43c7e3 100644 --- a/boa/src/syntax/parser/statement/continue_stm/mod.rs +++ b/boa/src/syntax/parser/statement/continue_stm/mod.rs @@ -11,9 +11,12 @@ mod tests; use super::LabelIdentifier; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, + }, + BoaProfiler, }; /// For statement parsing @@ -48,6 +51,7 @@ impl TokenParser for ContinueStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ContinueStatement", "Parsing"); cursor.expect(Keyword::Continue, "continue statement")?; let label = if let (true, tok) = cursor.peek_semicolon(false) { diff --git a/boa/src/syntax/parser/statement/declaration/hoistable.rs b/boa/src/syntax/parser/statement/declaration/hoistable.rs index 44a942a908e..37185e6377f 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable.rs @@ -5,12 +5,15 @@ //! //! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration -use crate::syntax::{ - ast::{node::FunctionDecl, Keyword, Node, Punctuator}, - parser::{ - function::FormalParameters, function::FunctionBody, statement::BindingIdentifier, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{node::FunctionDecl, Keyword, Node, Punctuator}, + parser::{ + function::FormalParameters, function::FunctionBody, statement::BindingIdentifier, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Hoistable declaration parsing. @@ -46,6 +49,7 @@ impl TokenParser for HoistableDeclaration { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing"); // TODO: check for generators and async functions + generators FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default) .parse(cursor) diff --git a/boa/src/syntax/parser/statement/declaration/lexical.rs b/boa/src/syntax/parser/statement/declaration/lexical.rs index ee9b3fd3f11..25dfec3a271 100644 --- a/boa/src/syntax/parser/statement/declaration/lexical.rs +++ b/boa/src/syntax/parser/statement/declaration/lexical.rs @@ -7,15 +7,18 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations -use crate::syntax::{ - ast::{ - node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node}, - Keyword, Punctuator, TokenKind, - }, - parser::{ - expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield, - Cursor, ParseError, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node}, + Keyword, Punctuator, TokenKind, + }, + parser::{ + expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield, + Cursor, ParseError, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Parses a lexical declaration. @@ -51,6 +54,7 @@ impl TokenParser for LexicalDeclaration { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("LexicalDeclaration", "Parsing"); let tok = cursor.next().ok_or(ParseError::AbruptEnd)?; match tok.kind { diff --git a/boa/src/syntax/parser/statement/declaration/mod.rs b/boa/src/syntax/parser/statement/declaration/mod.rs index c8f44aee9f7..c17cebe90e1 100644 --- a/boa/src/syntax/parser/statement/declaration/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/mod.rs @@ -13,9 +13,12 @@ mod lexical; mod tests; use self::{hoistable::HoistableDeclaration, lexical::LexicalDeclaration}; -use crate::syntax::{ - ast::{Keyword, Node, TokenKind}, - parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Keyword, Node, TokenKind}, + parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser}, + }, + BoaProfiler, }; /// Parses a declaration. @@ -47,6 +50,7 @@ impl TokenParser for Declaration { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("Declaration", "Parsing"); let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; match tok.kind { diff --git a/boa/src/syntax/parser/statement/if_stm/mod.rs b/boa/src/syntax/parser/statement/if_stm/mod.rs index 7d7f95785bb..68a2f065b8e 100644 --- a/boa/src/syntax/parser/statement/if_stm/mod.rs +++ b/boa/src/syntax/parser/statement/if_stm/mod.rs @@ -2,12 +2,15 @@ mod tests; use super::Statement; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{ - expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, - TokenParser, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{ + expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, + TokenParser, + }, }, + BoaProfiler, }; /// If statement parsing. @@ -47,6 +50,7 @@ impl TokenParser for IfStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("IfStatement", "Parsing"); cursor.expect(Keyword::If, "if statement")?; cursor.expect(Punctuator::OpenParen, "if statement")?; diff --git a/boa/src/syntax/parser/statement/iteration/do_while_statement.rs b/boa/src/syntax/parser/statement/iteration/do_while_statement.rs index 5b71869af4e..c42c37a0ab5 100644 --- a/boa/src/syntax/parser/statement/iteration/do_while_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/do_while_statement.rs @@ -7,12 +7,15 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while //! [spec]: https://tc39.es/ecma262/#sec-do-while-statement -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{ - expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, - ParseError, ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{ + expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, + Cursor, ParseError, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Do...while statement parsing @@ -54,6 +57,7 @@ impl TokenParser for DoWhileStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("DoWhileStatement", "Parsing"); cursor.expect(Keyword::Do, "do while statement")?; let body = diff --git a/boa/src/syntax/parser/statement/iteration/for_statement.rs b/boa/src/syntax/parser/statement/iteration/for_statement.rs index c5d2b340a06..347c7ad17d7 100644 --- a/boa/src/syntax/parser/statement/iteration/for_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/for_statement.rs @@ -7,17 +7,20 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for //! [spec]: https://tc39.es/ecma262/#sec-for-statement -use crate::syntax::{ - ast::{ - node::{ForLoop, Node}, - Const, Keyword, Punctuator, TokenKind, - }, - parser::{ - expression::Expression, - statement::declaration::Declaration, - statement::{variable::VariableDeclarationList, Statement}, - AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{ForLoop, Node}, + Const, Keyword, Punctuator, TokenKind, + }, + parser::{ + expression::Expression, + statement::declaration::Declaration, + statement::{variable::VariableDeclarationList, Statement}, + AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + }, }, + BoaProfiler, }; /// For statement parsing @@ -59,6 +62,7 @@ impl TokenParser for ForStatement { type Output = ForLoop; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("ForStatement", "Parsing"); cursor.expect(Keyword::For, "for statement")?; cursor.expect(Punctuator::OpenParen, "for statement")?; diff --git a/boa/src/syntax/parser/statement/iteration/while_statement.rs b/boa/src/syntax/parser/statement/iteration/while_statement.rs index dd8040ae248..e1e3cb0307c 100644 --- a/boa/src/syntax/parser/statement/iteration/while_statement.rs +++ b/boa/src/syntax/parser/statement/iteration/while_statement.rs @@ -1,9 +1,12 @@ -use crate::syntax::{ - ast::{Keyword, Node, Punctuator}, - parser::{ - expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor, - ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator}, + parser::{ + expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, + Cursor, ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// While statement parsing @@ -45,6 +48,7 @@ impl TokenParser for WhileStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("WhileStatement", "Parsing"); cursor.expect(Keyword::While, "while statement")?; cursor.expect(Punctuator::OpenParen, "while statement")?; diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index cf845e8a410..a4dd078fc5c 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -36,7 +36,10 @@ use super::{ expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }; -use crate::syntax::ast::{node, Keyword, Node, Punctuator, TokenKind}; +use crate::{ + syntax::ast::{node, Keyword, Node, Punctuator, TokenKind}, + BoaProfiler, +}; /// Statement parsing. /// @@ -90,6 +93,7 @@ impl TokenParser for Statement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("Statement", "Parsing"); // TODO: add BreakableStatement and divide Whiles, fors and so on to another place. let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; @@ -197,6 +201,7 @@ impl TokenParser for StatementList { type Output = node::StatementList; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("StatementList", "Parsing"); let mut items = Vec::new(); loop { @@ -270,6 +275,7 @@ impl TokenParser for StatementListItem { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("StatementListItem", "Parsing"); let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?; match tok.kind { @@ -315,6 +321,7 @@ impl TokenParser for ExpressionStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ExpressionStatement", "Parsing"); // TODO: lookahead let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; @@ -364,6 +371,7 @@ impl TokenParser for BindingIdentifier { type Output = Box; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("BindingIdentifier", "Parsing"); // TODO: strict mode. let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?; diff --git a/boa/src/syntax/parser/statement/return_stm/mod.rs b/boa/src/syntax/parser/statement/return_stm/mod.rs index 096b3be17ae..e3700ca8720 100644 --- a/boa/src/syntax/parser/statement/return_stm/mod.rs +++ b/boa/src/syntax/parser/statement/return_stm/mod.rs @@ -1,9 +1,14 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{ + expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, + }, + }, + BoaProfiler, }; /// Return statement parsing @@ -38,6 +43,7 @@ impl TokenParser for ReturnStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ReturnStatement", "Parsing"); cursor.expect(Keyword::Return, "return statement")?; if let (true, tok) = cursor.peek_semicolon(false) { diff --git a/boa/src/syntax/parser/statement/switch/mod.rs b/boa/src/syntax/parser/statement/switch/mod.rs index c8416149a03..75fd9209793 100644 --- a/boa/src/syntax/parser/statement/switch/mod.rs +++ b/boa/src/syntax/parser/statement/switch/mod.rs @@ -1,12 +1,15 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator}, - parser::{ - expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, - ParseResult, TokenParser, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator}, + parser::{ + expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, + ParseResult, TokenParser, + }, }, + BoaProfiler, }; /// Switch statement parsing. @@ -44,6 +47,7 @@ impl TokenParser for SwitchStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("SwitchStatement", "Parsing"); cursor.expect(Keyword::Switch, "switch statement")?; cursor.expect(Punctuator::OpenParen, "switch statement")?; diff --git a/boa/src/syntax/parser/statement/throw/mod.rs b/boa/src/syntax/parser/statement/throw/mod.rs index 4d788424e7e..aacc3d2216e 100644 --- a/boa/src/syntax/parser/statement/throw/mod.rs +++ b/boa/src/syntax/parser/statement/throw/mod.rs @@ -1,9 +1,14 @@ #[cfg(test)] mod tests; -use crate::syntax::{ - ast::{Keyword, Node, Punctuator, TokenKind}, - parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser}, +use crate::{ + syntax::{ + ast::{Keyword, Node, Punctuator, TokenKind}, + parser::{ + expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, + }, + }, + BoaProfiler, }; /// For statement parsing @@ -38,6 +43,7 @@ impl TokenParser for ThrowStatement { type Output = Node; fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult { + let _timer = BoaProfiler::global().start_event("ThrowStatement", "Parsing"); cursor.expect(Keyword::Throw, "throw statement")?; cursor.peek_expect_no_lineterminator(0)?; diff --git a/boa/src/syntax/parser/statement/try_stm/catch.rs b/boa/src/syntax/parser/statement/try_stm/catch.rs index 816db052adc..1e818400a0b 100644 --- a/boa/src/syntax/parser/statement/try_stm/catch.rs +++ b/boa/src/syntax/parser/statement/try_stm/catch.rs @@ -1,12 +1,15 @@ -use crate::syntax::{ - ast::{ - node::{self, Identifier}, - Keyword, Punctuator, - }, - parser::{ - statement::{block::Block, BindingIdentifier}, - AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{self, Identifier}, + Keyword, Punctuator, + }, + parser::{ + statement::{block::Block, BindingIdentifier}, + AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser, + }, }, + BoaProfiler, }; /// Catch parsing @@ -44,6 +47,7 @@ impl TokenParser for Catch { type Output = node::Catch; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("Catch", "Parsing"); cursor.expect(Keyword::Catch, "try statement")?; let catch_param = if cursor.next_if(Punctuator::OpenParen).is_some() { let catch_param = diff --git a/boa/src/syntax/parser/statement/try_stm/finally.rs b/boa/src/syntax/parser/statement/try_stm/finally.rs index a6c9bebbda9..ac4f39a395f 100644 --- a/boa/src/syntax/parser/statement/try_stm/finally.rs +++ b/boa/src/syntax/parser/statement/try_stm/finally.rs @@ -1,9 +1,12 @@ -use crate::syntax::{ - ast::{node, Keyword}, - parser::{ - statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, - TokenParser, +use crate::{ + syntax::{ + ast::{node, Keyword}, + parser::{ + statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, + TokenParser, + }, }, + BoaProfiler, }; /// Finally parsing @@ -41,6 +44,7 @@ impl TokenParser for Finally { type Output = node::Finally; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("Finally", "Parsing"); cursor.expect(Keyword::Finally, "try statement")?; Ok( Block::new(self.allow_yield, self.allow_await, self.allow_return) diff --git a/boa/src/syntax/parser/statement/try_stm/mod.rs b/boa/src/syntax/parser/statement/try_stm/mod.rs index 2e7c4902477..041148f329b 100644 --- a/boa/src/syntax/parser/statement/try_stm/mod.rs +++ b/boa/src/syntax/parser/statement/try_stm/mod.rs @@ -7,9 +7,12 @@ mod tests; use self::catch::Catch; use self::finally::Finally; use super::block::Block; -use crate::syntax::{ - ast::{node::Try, Keyword, TokenKind}, - parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, +use crate::{ + syntax::{ + ast::{node::Try, Keyword, TokenKind}, + parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser}, + }, + BoaProfiler, }; /// Try...catch statement parsing @@ -47,6 +50,7 @@ impl TokenParser for TryStatement { type Output = Try; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("TryStatement", "Parsing"); // TRY cursor.expect(Keyword::Try, "try statement")?; diff --git a/boa/src/syntax/parser/statement/variable.rs b/boa/src/syntax/parser/statement/variable.rs index a5f750140d9..904b012f0ce 100644 --- a/boa/src/syntax/parser/statement/variable.rs +++ b/boa/src/syntax/parser/statement/variable.rs @@ -1,13 +1,16 @@ // use super::lexical_declaration_continuation; -use crate::syntax::{ - ast::{ - node::{VarDecl, VarDeclList}, - Keyword, Punctuator, TokenKind, - }, - parser::{ - expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield, - Cursor, ParseError, TokenParser, +use crate::{ + syntax::{ + ast::{ + node::{VarDecl, VarDeclList}, + Keyword, Punctuator, TokenKind, + }, + parser::{ + expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield, + Cursor, ParseError, TokenParser, + }, }, + BoaProfiler, }; /// Variable statement parsing. @@ -44,6 +47,7 @@ impl TokenParser for VariableStatement { type Output = VarDeclList; fn parse(self, cursor: &mut Cursor<'_>) -> Result { + let _timer = BoaProfiler::global().start_event("VariableStatement", "Parsing"); cursor.expect(Keyword::Var, "variable statement")?; let decl_list = diff --git a/docs/img/profiler.png b/docs/img/profiler.png new file mode 100644 index 00000000000..edd8360f218 Binary files /dev/null and b/docs/img/profiler.png differ diff --git a/docs/profiling.md b/docs/profiling.md new file mode 100644 index 00000000000..ab59d9d20b7 --- /dev/null +++ b/docs/profiling.md @@ -0,0 +1,30 @@ +# Profiling + +![Example](img/profiler.png) + +It's possible to get a full profile of Boa in action. +Sometimes this is needed to figure out where it is spending most of it's time. + +We use a crate called [measureme](https://github.com/rust-lang/measureme), which helps us keep track of timing functions during runtime. + +When the "profiler" flag is enabled, you compile with the profiler and it is called throughout the interpreter. +when the feature flag is not enabled, you have an empty dummy implementation that is just no ops. rustc should completely optimize that away. So there should be no performance downgrade from these changes + +## Prerequesites + +- [Crox](https://github.com/rust-lang/measureme/blob/master/crox/Readme.md) + +## How To Use + +You can run boa using the "profiler" feature flag to enable profiling. Seeing as you'll most likely be using boa_cli you can pass this through, like so: + +`cargo run --features Boa/profiler ../tests/js/test.js` + +Once finished you should see some trace files left in the directory (boa_cli in this case). +In the same directory as the `.events, string_data, string_index` files run `crox my_trace` or whatever the name of the files are. This will generate a chrome_profiler.json file, you can load this into Chrome Dev tools. + +## More Info + +- https://blog.rust-lang.org/inside-rust/2020/02/25/intro-rustc-self-profile.html +- https://github.com/rust-lang/measureme +- https://github.com/rust-lang/measureme/blob/master/crox/Readme.md