Skip to main content

ezno_lib/transformers/
optimisations.rs

1use checker::FunctionId;
2use parser::{
3	declarations::{
4		classes::{ClassMember, ClassProperty},
5		ClassDeclaration,
6	},
7	expressions::object_literal::ObjectLiteralMember,
8	visiting::{BlockItemMut, VisitorMut},
9	ASTNode, Expression, ExpressionOrStatementPosition, SourceId, StatementOrDeclaration,
10};
11
12use crate::build::CheckingOutputWithoutDiagnostics;
13
14/// A transformer that optimises expression code
15/// - Removes dead functions
16///
17/// TODO this can still break somethings if functions are used but not called
18pub struct ExpressionOptimiser;
19
20impl VisitorMut<Expression, CheckingOutputWithoutDiagnostics> for ExpressionOptimiser {
21	fn visit_mut(
22		&mut self,
23		item: &mut Expression,
24		data: &mut CheckingOutputWithoutDiagnostics,
25		chain: &parser::visiting::Chain,
26	) {
27		match item {
28			Expression::ObjectLiteral(literal) => {
29				// TODO properties and even entire object
30				for item in literal.members.iter_mut() {
31					if let ObjectLiteralMember::Method(method) = item {
32						let position = method.get_position();
33						let function_id = FunctionId(chain.get_module(), position.start);
34						if !data.is_function_called(function_id) {
35							// Make it null for now to not break `Object.keys`
36							let key = method.name.clone();
37							*item = ObjectLiteralMember::Property {
38								key,
39								assignment: false,
40								value: Expression::Null(position),
41								position,
42							};
43						}
44					}
45				}
46			}
47			Expression::ArrowFunction(func) => {
48				if !data
49					.is_function_called(FunctionId(chain.get_module(), func.get_position().start))
50				{
51					*item = Expression::Null(func.get_position());
52				}
53			}
54			Expression::ExpressionFunction(func) => {
55				if !data
56					.is_function_called(FunctionId(chain.get_module(), func.get_position().start))
57				{
58					*item = Expression::Null(func.get_position());
59				}
60			}
61			Expression::ClassExpression(cls) => {
62				shake_class(cls, data, chain.get_module());
63			}
64			_ => {}
65		}
66	}
67}
68
69/// A transformer that optimises statement code
70/// - Removes dead functions
71pub struct StatementOptimiser;
72
73impl VisitorMut<BlockItemMut<'_>, CheckingOutputWithoutDiagnostics> for StatementOptimiser {
74	fn visit_mut(
75		&mut self,
76		item: &mut BlockItemMut,
77		data: &mut CheckingOutputWithoutDiagnostics,
78		chain: &parser::visiting::Chain,
79	) {
80		if let BlockItemMut::StatementOrDeclaration(StatementOrDeclaration::Declaration(
81			declaration,
82		)) = item
83		{
84			match declaration {
85				parser::Declaration::Variable(_) => {
86					// TODO remove if never read
87				}
88				parser::Declaration::Function(func) => {
89					if !data.is_function_called(FunctionId(
90						chain.get_module(),
91						func.get_position().start,
92					)) {
93						// Replace with property to not break Object.keys for now
94						// TODO replacing this with variable isn't great but
95						// is the unfortunate design of `StatementOrDeclarationMut`
96						*declaration = parser::Declaration::Variable(
97							parser::declarations::VariableDeclaration::LetDeclaration {
98								declarations: Vec::new(),
99								position: func.get_position(),
100							},
101						)
102					}
103				}
104				parser::Declaration::Class(cls) => {
105					shake_class(&mut cls.on, data, chain.get_module());
106				}
107				parser::Declaration::Import(_) => {
108					// TODO imported items
109				}
110				parser::Declaration::Export(_) => {
111					// TODO exported items
112				}
113				parser::Declaration::Enum(_)
114				| parser::Declaration::Interface(_)
115				| parser::Declaration::TypeAlias(_)
116				| parser::Declaration::DeclareVariable(_)
117				| parser::Declaration::Namespace(_) => {}
118			}
119		}
120	}
121}
122
123/// TODO properties and even entire class
124fn shake_class<T: ExpressionOrStatementPosition>(
125	class: &mut ClassDeclaration<T>,
126	data: &CheckingOutputWithoutDiagnostics,
127	source: SourceId,
128) {
129	for item in class.members.iter_mut() {
130		if let ClassMember::Method(is_static, func) = &item.on {
131			let id = FunctionId(source, func.position.start);
132			if !data.is_function_called(id) {
133				// Replace with property to not break Object.keys for now
134				item.on = ClassMember::Property(
135					*is_static,
136					ClassProperty {
137						is_readonly: false,
138						is_optional: false,
139						key: func.name.clone(),
140						type_annotation: None,
141						value: Some(Box::new(Expression::Null(func.position))),
142						position: func.position,
143					},
144				);
145			}
146		}
147	}
148}