Skip to content

Commit

Permalink
feat: introduce JavascriptParser (#5280)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvanjoi authored Jan 12, 2024
1 parent 19a61eb commit 96ae293
Show file tree
Hide file tree
Showing 13 changed files with 1,511 additions and 70 deletions.
7 changes: 7 additions & 0 deletions crates/rspack_plugin_javascript/src/utils/const/logic_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ use crate::visitors::common_js_import_dependency_scanner::CommonJsImportDependen

type KeepRight = bool;

pub fn is_logic_op(op: BinaryOp) -> bool {
matches!(
op,
BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing
)
}

/// Return:
/// - `None` means should walk left and right;
/// - `Some(true)` means should walk right;
Expand Down
2 changes: 1 addition & 1 deletion crates/rspack_plugin_javascript/src/utils/const/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ mod logic_expr;

// TODO: should move to const plugin
pub use if_stmt::statement_if;
pub use logic_expr::expression_logic_operator;
pub use logic_expr::{expression_logic_operator, is_logic_op};
25 changes: 19 additions & 6 deletions crates/rspack_plugin_javascript/src/utils/eval/eval_lit_expr.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use rspack_core::SpanExt;
use swc_core::common::Spanned;
use swc_core::ecma::ast::Lit;
use swc_core::ecma::ast::{Lit, PropName, Str};

use super::BasicEvaluatedExpression;

fn eval_str(str: &Str) -> BasicEvaluatedExpression {
let mut res = BasicEvaluatedExpression::with_range(str.span().real_lo(), str.span_hi().0);
res.set_string(str.value.to_string());
res
}

pub fn eval_lit_expr(expr: &Lit) -> Option<BasicEvaluatedExpression> {
match expr {
Lit::Str(str) => {
let mut res = BasicEvaluatedExpression::with_range(str.span().real_lo(), str.span_hi().0);
res.set_string(str.value.to_string());
Some(res)
}
Lit::Str(str) => Some(eval_str(str)),
Lit::Regex(regexp) => {
let mut res =
BasicEvaluatedExpression::with_range(regexp.span().real_lo(), regexp.span_hi().0);
Expand All @@ -26,3 +28,14 @@ pub fn eval_lit_expr(expr: &Lit) -> Option<BasicEvaluatedExpression> {
_ => None,
}
}

pub fn eval_prop_name(prop_name: &PropName) -> Option<BasicEvaluatedExpression> {
match prop_name {
PropName::Str(str) => Some(eval_str(str)),
// TODO:
PropName::Ident(_) => None,
PropName::Num(_) => None,
PropName::Computed(_) => None,
PropName::BigInt(_) => None,
}
}
19 changes: 14 additions & 5 deletions crates/rspack_plugin_javascript/src/utils/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rspack_core::DependencyLocation;
pub use self::eval_array_expr::eval_array_expression;
pub use self::eval_binary_expr::eval_binary_expression;
pub use self::eval_cond_expr::eval_cond_expression;
pub use self::eval_lit_expr::eval_lit_expr;
pub use self::eval_lit_expr::{eval_lit_expr, eval_prop_name};
pub use self::eval_new_expr::eval_new_expression;
pub use self::eval_tpl_expr::{eval_tpl_expression, TemplateStringKind};
pub use self::eval_unary_expr::eval_unary_expression;
Expand All @@ -31,7 +31,7 @@ enum Ty {
Array,
ConstArray,
BigInt,
// Identifier,
Identifier,
// TypeWrapped,
TemplateString,
}
Expand All @@ -58,6 +58,7 @@ pub struct BasicEvaluatedExpression {
string: Option<String>,
bigint: Option<Bigint>,
regexp: Option<Regexp>,
identifier: Option<String>,
items: Option<Vec<BasicEvaluatedExpression>>,
quasis: Option<Vec<BasicEvaluatedExpression>>,
parts: Option<Vec<BasicEvaluatedExpression>>,
Expand Down Expand Up @@ -87,6 +88,7 @@ impl BasicEvaluatedExpression {
bigint: None,
quasis: None,
parts: None,
identifier: None,
template_string_kind: None,
options: None,
string: None,
Expand All @@ -105,9 +107,9 @@ impl BasicEvaluatedExpression {
// matches!(self.ty, Ty::Unknown)
// }

// pub fn is_identifier(&self) -> bool {
// matches!(self.ty, Ty::Identifier)
// }
pub fn is_identifier(&self) -> bool {
matches!(self.ty, Ty::Identifier)
}

pub fn is_null(&self) -> bool {
matches!(self.ty, Ty::Null)
Expand Down Expand Up @@ -319,6 +321,13 @@ impl BasicEvaluatedExpression {
self.string.as_ref().expect("make sure string exist")
}

pub fn identifier(&self) -> &String {
self
.identifier
.as_ref()
.expect("make sure identifier exist")
}

pub fn regexp(&self) -> &Regexp {
self.regexp.as_ref().expect("make sure regexp exist")
}
Expand Down
17 changes: 0 additions & 17 deletions crates/rspack_plugin_javascript/src/visitors/clear_mark.rs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod hot_module_replacement_scanner;
mod import_meta_scanner;
mod import_scanner;
mod node_stuff_scanner;
mod parser;
mod url_scanner;
mod util;
mod worker_scanner;
Expand All @@ -27,9 +28,10 @@ use rustc_hash::FxHashMap as HashMap;
use swc_core::common::Span;
use swc_core::common::{comments::Comments, Mark, SyntaxContext};
use swc_core::ecma::atoms::JsWord;
pub use util::*;

use self::harmony_import_dependency_scanner::ImportMap;
use self::parser::JavascriptParser;
pub use self::util::*;
use self::{
api_scanner::ApiScanner, common_js_export_scanner::CommonJsExportDependencyScanner,
common_js_import_dependency_scanner::CommonJsImportDependencyScanner,
Expand Down Expand Up @@ -86,6 +88,9 @@ pub fn scan_dependencies(

let mut rewrite_usage_span = HashMap::default();

let mut parser = JavascriptParser::new(&mut dependencies, &mut presentational_dependencies);
parser.visit(program.get_inner_program());

// TODO it should enable at js/auto or js/dynamic, but builtins provider will inject require at esm
// https://github.com/web-infra-dev/rspack/issues/3544
program.visit_with(&mut CommonJsImportDependencyScanner::new(
Expand Down
159 changes: 159 additions & 0 deletions crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#![allow(unused)]

mod walk;
mod walk_block_pre;
mod walk_pre;

use std::borrow::Cow;

use rspack_core::{BoxDependency, DependencyTemplate};
use swc_core::ecma::ast::{ArrayPat, AssignPat, BlockStmt, ObjectPat, ObjectPatProp, Pat, Program};
use swc_core::ecma::ast::{Ident, Lit, RestPat};

use crate::visitors::scope_info::{ScopeInfoDB, ScopeInfoId};

pub struct JavascriptParser<'parser> {
pub(crate) dependencies: &'parser mut Vec<BoxDependency>,
pub(crate) presentational_dependencies: &'parser mut Vec<Box<dyn DependencyTemplate>>,
pub(super) definitions_db: ScopeInfoDB,
// ===== scope info =======
pub(crate) in_try: bool,
pub(crate) in_if: bool,
pub(crate) in_short_hand: bool,
pub(super) definitions: ScopeInfoId,
}

impl<'parser> JavascriptParser<'parser> {
pub fn new(
dependencies: &'parser mut Vec<BoxDependency>,
presentational_dependencies: &'parser mut Vec<Box<dyn DependencyTemplate>>,
) -> Self {
let mut db = ScopeInfoDB::new();
Self {
dependencies,
presentational_dependencies,
in_try: false,
in_if: false,
in_short_hand: false,
definitions: db.create(),
definitions_db: db,
}
}

fn define_variable(&mut self, name: String) {
if let Some(id) = self.definitions_db.get(&self.definitions, &name)
&& self.definitions == id
{
return;
}
self.definitions_db.set(self.definitions, name)
}

fn undefined_variable(&mut self, name: String) {
self.definitions_db.delete(self.definitions, name)
}

fn enter_ident<F>(&mut self, ident: &Ident, on_ident: F)
where
F: FnOnce(&mut Self, &Ident),
{
// TODO: add hooks here;
on_ident(self, ident);
}

fn enter_array_pattern<F>(&mut self, array_pat: &ArrayPat, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
{
array_pat
.elems
.iter()
.flatten()
.for_each(|ele| self.enter_pattern(Cow::Borrowed(ele), on_ident));
}

fn enter_assignment_pattern<F>(&mut self, assign: &AssignPat, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
{
self.enter_pattern(Cow::Borrowed(&assign.left), on_ident);
}

fn enter_object_pattern<F>(&mut self, obj: &ObjectPat, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
{
for prop in &obj.props {
match prop {
ObjectPatProp::KeyValue(kv) => self.enter_pattern(Cow::Borrowed(&kv.value), on_ident),
ObjectPatProp::Assign(assign) => self.enter_ident(&assign.key, on_ident),
ObjectPatProp::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
}
}
}

fn enter_rest_pattern<F>(&mut self, rest: &RestPat, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
{
self.enter_pattern(Cow::Borrowed(&rest.arg), on_ident)
}

fn enter_pattern<F>(&mut self, pattern: Cow<Pat>, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
{
match &*pattern {
Pat::Ident(ident) => self.enter_ident(&ident.id, on_ident),
Pat::Array(array) => self.enter_array_pattern(array, on_ident),
Pat::Assign(assign) => self.enter_assignment_pattern(assign, on_ident),
Pat::Object(obj) => self.enter_object_pattern(obj, on_ident),
Pat::Rest(rest) => self.enter_rest_pattern(rest, on_ident),
Pat::Invalid(_) => (),
Pat::Expr(_) => (),
}
}

fn enter_patterns<'a, I, F>(&mut self, patterns: I, on_ident: F)
where
F: FnOnce(&mut Self, &Ident) + Copy,
I: Iterator<Item = Cow<'a, Pat>>,
{
for pattern in patterns {
self.enter_pattern(pattern, on_ident);
}
}

pub fn visit(&mut self, ast: &Program) {
// TODO: `hooks.program.call`
match ast {
Program::Module(m) => {
self.pre_walk_module_declarations(&m.body);
self.block_pre_walk_module_declarations(&m.body);
self.walk_module_declarations(&m.body);
}
Program::Script(s) => {
self.pre_walk_statements(&s.body);
self.block_pre_walk_statements(&s.body);
self.walk_statements(&s.body);
}
};
// TODO: `hooks.finish.call`
}

fn detect_mode(&mut self, stmt: &BlockStmt) {
let Some(Lit::Str(str)) = stmt
.stmts
.first()
.and_then(|stmt| stmt.as_expr())
.and_then(|expr_stmt| expr_stmt.expr.as_lit())
else {
return;
};

if str.value.as_str() == "use strict" {
let current_scope = self.definitions_db.expect_get_mut(&self.definitions);
current_scope.is_strict = true;
}
}
}
Loading

1 comment on commit 96ae293

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

name base current %
10000_development-mode + exec 1.67 s ± 30 ms 1.63 s ± 20 ms -1.95%
10000_development-mode_hmr + exec 998 ms ± 11 ms 996 ms ± 18 ms -0.19%
10000_production-mode + exec 2.85 s ± 78 ms 2.8 s ± 29 ms -1.69%
threejs_development-mode_10x + exec 2.01 s ± 31 ms 2 s ± 17 ms -0.52%
threejs_development-mode_10x_hmr + exec 1.31 s ± 5 ms 1.32 s ± 30 ms +0.65%
threejs_production-mode_10x + exec 6.04 s ± 125 ms 5.96 s ± 32 ms -1.31%

Please sign in to comment.