diff --git a/.gitignore b/.gitignore index 42c96f2e8c..972d1eb0f8 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 49d8508262..aec1c2cd7b 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 3bc70dc154..486605160d 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 1089abba35..aaa167436a 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,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 aebe579223..a92820c558 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 7dd3461116..646011e841 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -20,6 +20,7 @@ use crate::{ value::{same_value_zero, ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use std::{ borrow::Borrow, @@ -1041,6 +1042,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 fabb2be33f..f48a507118 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -19,6 +19,7 @@ use crate::{ }, exec::Interpreter, syntax::ast::bigint::BigInt as AstBigInt, + BoaProfiler, }; #[cfg(test)] @@ -138,6 +139,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 40b7de056d..76012a40e7 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 64428f0e9c..aaf8261abe 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; @@ -554,5 +555,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 d142994c7d..a6df97025b 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; @@ -81,6 +82,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 afd85021ee..0af07ff45a 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. @@ -71,6 +72,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 bea69bbbf8..5e7a5ae973 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 f656b09a0a..87eb35f436 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)] @@ -174,5 +174,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 04591cad30..775642ae29 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 d51ac0f996..6b0bf116c1 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -26,6 +26,7 @@ use crate::{ value::{ResultValue, Value, ValueData}, }, exec::Interpreter, + BoaProfiler, }; use num_traits::float::FloatCore; use std::{borrow::Borrow, ops::Deref}; @@ -435,6 +436,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 d99531bed8..47b21dd282 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 9586509b2c..c367fdab63 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; @@ -131,6 +132,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(), @@ -146,6 +148,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) { @@ -352,6 +355,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(), @@ -640,5 +644,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 16fd0bfefe..684763ff0f 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 73833263f6..1632c4855a 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 e71a5dc09c..bbf915bfb4 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 98682c783c..8deadd3b63 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) } } @@ -164,6 +165,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 192754a687..4def097028 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::{ @@ -124,6 +124,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); @@ -393,6 +394,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" { @@ -439,6 +441,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, @@ -459,6 +462,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(); @@ -484,6 +488,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) => { @@ -582,6 +587,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() } @@ -592,6 +598,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(); @@ -621,6 +628,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 @@ -766,6 +774,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 65919b446e..8e32c09945 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 548acd461f..1f0374f9bb 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 c592a05ab9..deec6e432b 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 0ab755872e..192a8a168f 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 f81a3b1153..d313ebbf12 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 ab1f8308d2..dd4f728629 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 e08ce8b701..ea24f09e6d 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,6 +29,7 @@ use crate::{ constant::Const, node::{FormalParameter, MethodDefinitionKind, Node, PropertyDefinition, StatementList}, }, + BoaProfiler, }; use std::{borrow::Borrow, ops::Deref}; @@ -362,6 +363,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 83bad26f0a..cecb2fdea5 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 31558e383f..4863232171 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 8b45e46e1a..cb6b25a43f 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 ed75621262..4314d77f79 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 0000000000..43c7ccbf1f --- /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 44c9ac952a..b6cfc4decf 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 25431ed481..3d370d4029 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 ccab9a104b..608817cf00 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 2bea95cc9b..c258402017 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 fdc54114a3..811bde4537 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 b45f35880d..4f71f21736 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 a5caf3a938..4ccdde463b 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 899b1cc549..8f32a8a8b6 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 de13301eaf..842434740a 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 737dfdbe85..1be558b9ec 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 87317a6df5..0b4137bc15 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 999af9a8c1..8bc1e74ace 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 f27bcab26b..8ebbf7347e 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 f1c6785400..eaeded52d5 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 3db21706cb..843cb0341e 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 d203097b05..9c7b9e1462 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 7da0b5414c..8614a43c7e 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 44a942a908..37185e6377 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 ee9b3fd3f1..25dfec3a27 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 c8f44aee9f..c17cebe90e 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 7d7f95785b..68a2f065b8 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 5b71869af4..c42c37a0ab 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 c5d2b340a0..347c7ad17d 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 dd8040ae24..e1e3cb0307 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 cf845e8a41..a4dd078fc5 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 096b3be17a..e3700ca872 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 c8416149a0..75fd920979 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 4d788424e7..aacc3d2216 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 816db052ad..1e818400a0 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 a6c9bebbda..ac4f39a395 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 2e7c490247..041148f329 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 a5f750140d..904b012f0c 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 0000000000..edd8360f21 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 0000000000..ab59d9d20b --- /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