From 878a63178ac4f7607c2bdd8439b131f0618b158e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Santos?= Date: Sun, 1 May 2022 12:26:43 +0000 Subject: [PATCH] garbage collect the AST while outputting code (#1189) * garbage collect the AST while outputting code * go through all the scopes before including source map sources in the source map --- lib/ast.js | 12 +----------- lib/minify.js | 28 +++++++++++++++++++++++++++- lib/output.js | 17 +++++++++++++++++ test/benchmark.js | 8 ++++---- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/lib/ast.js b/lib/ast.js index 29e8f79e9..625999990 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -552,11 +552,10 @@ var AST_With = DEFNODE("With", "expression", function AST_With(props) { var AST_Scope = DEFNODE( "Scope", - "variables functions uses_with uses_eval parent_scope enclosed cname", + "variables uses_with uses_eval parent_scope enclosed cname", function AST_Scope(props) { if (props) { this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -612,7 +611,6 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", function AST_Toplevel(props) { if (props) { this.globals = props.globals; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -694,7 +692,6 @@ var AST_Lambda = DEFNODE( this.is_generator = props.is_generator; this.async = props.async; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -774,7 +771,6 @@ var AST_Accessor = DEFNODE("Accessor", null, function AST_Accessor(props) { this.is_generator = props.is_generator; this.async = props.async; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -799,7 +795,6 @@ var AST_Function = DEFNODE("Function", null, function AST_Function(props) { this.is_generator = props.is_generator; this.async = props.async; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -824,7 +819,6 @@ var AST_Arrow = DEFNODE("Arrow", null, function AST_Arrow(props) { this.is_generator = props.is_generator; this.async = props.async; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -849,7 +843,6 @@ var AST_Defun = DEFNODE("Defun", null, function AST_Defun(props) { this.is_generator = props.is_generator; this.async = props.async; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -2161,7 +2154,6 @@ var AST_Class = DEFNODE("Class", "name extends properties", function AST_Class(p this.extends = props.extends; this.properties = props.properties; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -2255,7 +2247,6 @@ var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) { this.extends = props.extends; this.properties = props.properties; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; @@ -2278,7 +2269,6 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExp this.extends = props.extends; this.properties = props.properties; this.variables = props.variables; - this.functions = props.functions; this.uses_with = props.uses_with; this.uses_eval = props.uses_eval; this.parent_scope = props.parent_scope; diff --git a/lib/minify.js b/lib/minify.js index 82d9ca3dd..c112a17d4 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -7,7 +7,7 @@ import { map_to_object, HOP, } from "./utils/index.js"; -import { AST_Toplevel, AST_Node } from "./ast.js"; +import { AST_Toplevel, AST_Node, walk, AST_Scope } from "./ast.js"; import { parse } from "./parse.js"; import { OutputStream } from "./output.js"; import { Compressor } from "./compress/index.js"; @@ -194,6 +194,8 @@ async function minify(files, options, _fs_module) { url: null, }, true); } + + // -- Parse phase -- if (timings) timings.parse = Date.now(); var toplevel; if (files instanceof AST_Toplevel) { @@ -243,12 +245,16 @@ async function minify(files, options, _fs_module) { toplevel.figure_out_scope(options.mangle); toplevel.expand_names(options.mangle); } + + // -- Compress phase -- if (timings) timings.compress = Date.now(); if (options.compress) { toplevel = new Compressor(options.compress, { mangle_options: options.mangle }).compress(toplevel); } + + // -- Mangle phase -- if (timings) timings.scope = Date.now(); if (options.mangle) toplevel.figure_out_scope(options.mangle); if (timings) timings.mangle = Date.now(); @@ -261,6 +267,8 @@ async function minify(files, options, _fs_module) { if (options.mangle && options.mangle.properties) { toplevel = mangle_properties(toplevel, options.mangle.properties); } + + // Format phase if (timings) timings.format = Date.now(); var result = {}; if (options.format.ast) { @@ -270,6 +278,24 @@ async function minify(files, options, _fs_module) { result.ast = toplevel.to_mozilla_ast(); } if (!HOP(options.format, "code") || options.format.code) { + if (!options.format.ast) { + // Destroy stuff to save RAM. (unless the deprecated `ast` option is on) + options.format._destroy_ast = true; + + walk(toplevel, node => { + if (node instanceof AST_Scope) { + node.variables = undefined; + node.enclosed = undefined; + node.parent_scope = undefined; + } + if (node.block_scope) { + node.block_scope.variables = undefined; + node.block_scope.enclosed = undefined; + node.parent_scope = undefined; + } + }); + } + if (options.sourceMap) { options.format.source_map = await SourceMap({ file: options.sourceMap.filename, diff --git a/lib/output.js b/lib/output.js index 05d3e620b..5c6af2add 100644 --- a/lib/output.js +++ b/lib/output.js @@ -247,6 +247,8 @@ function OutputStream(options) { width : 80, wrap_iife : false, wrap_func_args : true, + + _destroy_ast : false }, true); if (options.shorthand === undefined) @@ -796,6 +798,18 @@ function OutputStream(options) { if (OUTPUT.length() > insert) newline_insert = insert; } + /** + * When output.option("_destroy_ast") is enabled, destroy the function. + * Call this after printing it. + */ + const gc_scope = + options["_destroy_ast"] + ? function gc_scope(scope) { + scope.body.length = 0; + scope.argnames.length = 0; + } + : noop; + var stack = []; return { get : get, @@ -842,6 +856,7 @@ function OutputStream(options) { with_square : with_square, add_mapping : add_mapping, option : function(opt) { return options[opt]; }, + gc_scope, printed_comments: printed_comments, prepend_comments: readonly ? noop : prepend_comments, append_comments : readonly || comment_filter === return_false ? noop : append_comments, @@ -1358,6 +1373,7 @@ function OutputStream(options) { }); DEFPRINT(AST_Lambda, function(self, output) { self._do_print(output); + output.gc_scope(self); }); DEFPRINT(AST_PrefixedTemplateString, function(self, output) { @@ -1437,6 +1453,7 @@ function OutputStream(options) { print_braced(self, output); } if (needs_parens) { output.print(")"); } + output.gc_scope(self); }); /* -----[ exits ]----- */ diff --git a/test/benchmark.js b/test/benchmark.js index 45769600e..0578d726b 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -3,10 +3,10 @@ "use strict"; -var createHash = require("crypto").createHash; -var fetch = require("./fetch"); -var fork = require("child_process").fork; -var zlib = require("zlib"); +import { createHash } from "crypto"; +import fetch from "./fetch.cjs"; +import { fork } from "child_process"; +import zlib from "zlib"; var args = process.argv.slice(2); if (!args.length) { args.push("-mc");