feat(codegen)!: remove const generic MINIFY (#5001)

This is a premature optimization, makes the code complicated, and bloats the final binary size.

The minify option is moved to `CodegenOptions`
This commit is contained in:
Boshen 2024-08-20 08:13:27 +00:00
parent b2ff2df5af
commit ce4d4698b4
17 changed files with 478 additions and 479 deletions

View file

@ -2,7 +2,7 @@ use std::{mem, ops::ControlFlow, path::Path};
use oxc_allocator::Allocator;
use oxc_ast::{ast::Program, Trivias};
use oxc_codegen::{CodeGenerator, CodegenOptions, CommentOptions, WhitespaceRemover};
use oxc_codegen::{CodeGenerator, CodegenOptions, CommentOptions};
use oxc_diagnostics::OxcDiagnostic;
use oxc_parser::{ParseOptions, Parser, ParserReturn};
use oxc_span::SourceType;
@ -216,14 +216,10 @@ pub trait CompilerInterface {
) -> String {
let comment_options = CommentOptions { preserve_annotate_comments: true };
if self.remove_whitespace() {
WhitespaceRemover::new().with_options(options).build(program).source_text
} else {
CodeGenerator::new()
.with_options(options)
.enable_comment(source_text, trivias.clone(), comment_options)
.build(program)
.source_text
}
CodeGenerator::new()
.with_options(CodegenOptions { minify: self.remove_whitespace(), ..options })
.enable_comment(source_text, trivias.clone(), comment_options)
.build(program)
.source_text
}
}

View file

@ -2,7 +2,7 @@
use std::{env, path::Path};
use oxc_allocator::Allocator;
use oxc_codegen::{CodeGenerator, CommentOptions, WhitespaceRemover};
use oxc_codegen::{CodeGenerator, CodegenOptions, CommentOptions};
use oxc_parser::Parser;
use oxc_span::SourceType;
use pico_args::Arguments;
@ -68,7 +68,10 @@ fn main() -> std::io::Result<()> {
if minify {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, &source_text, source_type).parse();
let minified = WhitespaceRemover::new().build(&ret.program).source_text;
let minified = CodeGenerator::new()
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })
.build(&ret.program)
.source_text;
println!("Minified:");
println!("{minified}");
}

View file

@ -45,7 +45,7 @@ impl From<(Comment, AnnotationKind)> for AnnotationComment {
}
}
impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
impl<'a> Codegen<'a> {
pub(crate) fn get_leading_annotate_comments(
&mut self,
node_start: u32,

View file

@ -47,8 +47,8 @@ pub enum BinaryishOperator {
Logical(LogicalOperator),
}
impl<const MINIFY: bool> Gen<MINIFY> for BinaryishOperator {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
impl Gen for BinaryishOperator {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
Self::Binary(op) => op.gen(p, ctx),
Self::Logical(op) => op.gen(p, ctx),
@ -89,7 +89,7 @@ pub struct BinaryExpressionVisitor<'a> {
}
impl<'a> BinaryExpressionVisitor<'a> {
pub fn gen_expr<const MINIFY: bool>(v: Self, p: &mut Codegen<'a, { MINIFY }>) {
pub fn gen_expr(v: Self, p: &mut Codegen<'a>) {
let mut v = v;
let stack_bottom = p.binary_expr_stack.len();
loop {
@ -133,7 +133,7 @@ impl<'a> BinaryExpressionVisitor<'a> {
}
}
pub fn check_and_prepare<const MINIFY: bool>(&mut self, p: &mut Codegen<{ MINIFY }>) -> bool {
pub fn check_and_prepare(&mut self, p: &mut Codegen) -> bool {
let e = self.e;
self.operator = e.operator();
@ -181,7 +181,7 @@ impl<'a> BinaryExpressionVisitor<'a> {
true
}
pub fn visit_right_and_finish<const MINIFY: bool>(&self, p: &mut Codegen<{ MINIFY }>) {
pub fn visit_right_and_finish(&self, p: &mut Codegen) {
p.print_soft_space();
self.operator.gen(p, Context::empty());
p.print_soft_space();

File diff suppressed because it is too large Load diff

View file

@ -34,15 +34,14 @@ pub use crate::{
};
/// Code generator without whitespace removal.
pub type CodeGenerator<'a> = Codegen<'a, false>;
/// Code generator with whitespace removal.
pub type WhitespaceRemover<'a> = Codegen<'a, true>;
pub type CodeGenerator<'a> = Codegen<'a>;
#[derive(Default, Clone, Copy)]
pub struct CodegenOptions {
/// Use single quotes instead of double quotes.
pub single_quote: bool,
pub minify: bool,
}
#[derive(Default, Clone, Copy)]
@ -56,7 +55,7 @@ pub struct CodegenReturn {
pub source_map: Option<oxc_sourcemap::SourceMap>,
}
pub struct Codegen<'a, const MINIFY: bool> {
pub struct Codegen<'a> {
options: CodegenOptions,
comment_options: CommentOptions,
@ -97,26 +96,26 @@ pub struct Codegen<'a, const MINIFY: bool> {
latest_consumed_comment_end: u32,
}
impl<'a, const MINIFY: bool> Default for Codegen<'a, MINIFY> {
impl<'a> Default for Codegen<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a, const MINIFY: bool> From<Codegen<'a, MINIFY>> for String {
fn from(mut val: Codegen<'a, MINIFY>) -> Self {
impl<'a> From<Codegen<'a>> for String {
fn from(mut val: Codegen<'a>) -> Self {
val.into_source_text()
}
}
impl<'a, const MINIFY: bool> From<Codegen<'a, MINIFY>> for Cow<'a, str> {
fn from(mut val: Codegen<'a, MINIFY>) -> Self {
impl<'a> From<Codegen<'a>> for Cow<'a, str> {
fn from(mut val: Codegen<'a>) -> Self {
Cow::Owned(val.into_source_text())
}
}
// Public APIs
impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
impl<'a> Codegen<'a> {
#[must_use]
pub fn new() -> Self {
Self {
@ -147,7 +146,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
/// Minification will reduce by at least half of the original size.
#[must_use]
pub fn with_capacity(mut self, source_text_len: usize) -> Self {
let capacity = if MINIFY { source_text_len / 2 } else { source_text_len };
let capacity = if self.options.minify { source_text_len / 2 } else { source_text_len };
self.code = Vec::with_capacity(capacity);
self
}
@ -215,7 +214,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
}
// Private APIs
impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
impl<'a> Codegen<'a> {
fn code(&self) -> &Vec<u8> {
&self.code
}
@ -226,7 +225,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
#[inline]
fn print_soft_space(&mut self) {
if !MINIFY {
if !self.options.minify {
self.print_char(b' ');
}
}
@ -238,7 +237,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
#[inline]
fn print_soft_newline(&mut self) {
if !MINIFY {
if !self.options.minify {
self.print_char(b'\n');
}
}
@ -271,21 +270,21 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
#[inline]
fn indent(&mut self) {
if !MINIFY {
if !self.options.minify {
self.indent += 1;
}
}
#[inline]
fn dedent(&mut self) {
if !MINIFY {
if !self.options.minify {
self.indent -= 1;
}
}
#[inline]
fn print_indent(&mut self) {
if MINIFY {
if self.options.minify {
return;
}
if self.print_next_indent_as_space {
@ -298,7 +297,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
#[inline]
fn print_semicolon_after_statement(&mut self) {
if MINIFY {
if self.options.minify {
self.needs_semicolon = true;
} else {
self.print_str(";\n");
@ -328,7 +327,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
self.print_char(b'=');
}
fn print_sequence<T: Gen<MINIFY>>(&mut self, items: &[T], ctx: Context) {
fn print_sequence<T: Gen>(&mut self, items: &[T], ctx: Context) {
for item in items {
item.gen(self, ctx);
self.print_comma();
@ -377,7 +376,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
self.print_soft_newline();
}
stmt => {
if need_space && MINIFY {
if need_space && self.options.minify {
self.print_hard_space();
}
self.print_next_indent_as_space = true;
@ -396,7 +395,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
self.needs_semicolon = false;
}
fn print_list<T: Gen<MINIFY>>(&mut self, items: &[T], ctx: Context) {
fn print_list<T: Gen>(&mut self, items: &[T], ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
self.print_comma();
@ -411,12 +410,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
expr.gen_expr(self, Precedence::Lowest, Context::empty());
}
fn print_expressions<T: GenExpr<MINIFY>>(
&mut self,
items: &[T],
precedence: Precedence,
ctx: Context,
) {
fn print_expressions<T: GenExpr>(&mut self, items: &[T], precedence: Precedence, ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
self.print_comma();
@ -513,7 +507,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
}
// Comment related
impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
impl<'a> Codegen<'a> {
/// Avoid issue related to rustc borrow checker .
/// Since if you want to print a range of source code, you need to borrow the source code
/// as immutable first, and call the [Self::print_str] which is a mutable borrow.

View file

@ -1,5 +1,5 @@
use oxc_allocator::Allocator;
use oxc_codegen::{CodeGenerator, CommentOptions, WhitespaceRemover};
use oxc_codegen::{CodeGenerator, CodegenOptions, CommentOptions};
use oxc_parser::Parser;
use oxc_span::SourceType;
@ -25,7 +25,10 @@ pub fn test_minify(source_text: &str, expected: &str) {
let source_type = SourceType::default().with_module(true).with_jsx(true);
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let result = WhitespaceRemover::new().build(&ret.program).source_text;
let result = CodeGenerator::new()
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })
.build(&ret.program)
.source_text;
assert_eq!(
result, expected,
"\nfor minify source {source_text}\nexpect {expected}\ngot {result:?}"

View file

@ -10,7 +10,7 @@ fn codegen(source_text: &str) -> String {
let source_type = SourceType::default().with_typescript(true).with_module(true);
let ret = Parser::new(&allocator, source_text, source_type).parse();
CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true })
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
.build(&ret.program)
.source_text
}

View file

@ -170,7 +170,8 @@ impl<'c, 'a: 'c> RuleFixer<'c, 'a> {
#[allow(clippy::unused_self)]
pub fn codegen(self) -> CodeGenerator<'a> {
CodeGenerator::new().with_options(CodegenOptions { single_quote: true })
CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
}
#[allow(clippy::unused_self)]

View file

@ -26,7 +26,7 @@ fn run(source_text: &str, source_type: SourceType, options: Option<CompressOptio
Compressor::new(&allocator, options).build(program);
}
CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true })
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
.build(program)
.source_text
}

View file

@ -22,7 +22,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariab
.into_symbol_table_and_scope_tree();
InjectGlobalVariables::new(&allocator, config).build(&mut symbols, &mut scopes, program);
let result = CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true })
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
.build(program)
.source_text;
let expected = run(expected, source_type, None);

View file

@ -13,7 +13,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefin
let program = allocator.alloc(ret.program);
ReplaceGlobalDefines::new(&allocator, config).build(program);
let result = CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true })
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
.build(program)
.source_text;
let expected = run(expected, source_type, None);

View file

@ -8,7 +8,7 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc};
use oxc::{
allocator::Allocator,
ast::{CommentKind, Trivias},
codegen::{CodeGenerator, WhitespaceRemover},
codegen::{CodeGenerator, CodegenOptions},
diagnostics::Error,
minifier::{CompressOptions, Minifier, MinifierOptions},
parser::{ParseOptions, Parser},
@ -293,11 +293,13 @@ impl Oxc {
Minifier::new(options).build(&allocator, program);
}
self.codegen_text = if minifier_options.whitespace() {
WhitespaceRemover::new().build(program).source_text
} else {
CodeGenerator::new().build(program).source_text
};
self.codegen_text = CodeGenerator::new()
.with_options(CodegenOptions {
minify: minifier_options.whitespace(),
..CodegenOptions::default()
})
.build(program)
.source_text;
Ok(())
}

View file

@ -121,8 +121,8 @@ impl<'a> TransformContext<'a> {
self.program.borrow_mut()
}
pub fn codegen<const MINIFY: bool>(&self) -> Codegen<'a, MINIFY> {
let codegen = Codegen::<MINIFY>::new();
pub fn codegen(&self) -> Codegen<'a> {
let codegen = Codegen::new();
if self.source_map {
codegen.enable_source_map(self.file_name(), self.source_text())
} else {

View file

@ -32,5 +32,5 @@ pub fn isolated_declaration(filename: String, source_text: String) -> IsolatedDe
pub(crate) fn build_declarations(ctx: &TransformContext<'_>) -> CodegenReturn {
let transformed_ret = IsolatedDeclarations::new(ctx.allocator).build(&ctx.program());
ctx.add_diagnostics(transformed_ret.errors);
ctx.codegen::<false>().build(&transformed_ret.program)
ctx.codegen().build(&transformed_ret.program)
}

View file

@ -108,5 +108,5 @@ fn transpile(ctx: &TransformContext<'_>) -> CodegenReturn {
.build(&mut ctx.program_mut());
ctx.add_diagnostics(ret.errors);
ctx.codegen::<false>().build(&ctx.program())
ctx.codegen().build(&ctx.program())
}

View file

@ -8,7 +8,7 @@ use std::{
use flate2::{write::GzEncoder, Compression};
use humansize::{format_size, DECIMAL};
use oxc_allocator::Allocator;
use oxc_codegen::WhitespaceRemover;
use oxc_codegen::{CodeGenerator, CodegenOptions};
use oxc_minifier::{CompressOptions, Minifier, MinifierOptions};
use oxc_parser::Parser;
use oxc_span::SourceType;
@ -111,7 +111,11 @@ fn minify(source_text: &str, source_type: SourceType, options: MinifierOptions)
let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program);
let ret = Minifier::new(options).build(&allocator, program);
WhitespaceRemover::new().with_mangler(ret.mangler).build(program).source_text
CodeGenerator::new()
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })
.with_mangler(ret.mangler)
.build(program)
.source_text
}
fn gzip_size(s: &str) -> usize {