33import * as acorn from 'acorn' ;
44import * as terser from '../third_party/terser/terser.js' ;
55import * as fs from 'node:fs' ;
6+ import assert from 'node:assert' ;
67
78// Utilities
89
9- function print ( x ) {
10- process . stdout . write ( x + '\n' ) ;
11- }
12-
1310function read ( x ) {
14- return fs . readFileSync ( x ) . toString ( ) ;
15- }
16-
17- function assert ( condition , text ) {
18- if ( ! condition ) {
19- throw new Error ( text ) ;
20- }
11+ return fs . readFileSync ( x , 'utf-8' ) ;
2112}
2213
2314function assertAt ( condition , node , message = '' ) {
2415 if ( ! condition ) {
25- const loc = acorn . getLineInfo ( input , node . start ) ;
26- throw new Error (
27- `${ infile } :${ loc . line } : ${ message } (use EMCC_DEBUG_SAVE=1 to preserve temporary inputs)` ,
28- ) ;
16+ if ( ! process . env . EMCC_DEBUG_SAVE ) {
17+ message += ' (use EMCC_DEBUG_SAVE=1 to preserve temporary inputs)' ;
18+ }
19+ let err = new Error ( message ) ;
20+ err [ 'loc' ] = acorn . getLineInfo ( input , node . start ) ;
21+ throw err ;
2922 }
3023}
3124
@@ -39,7 +32,7 @@ function visitChildren(node, c) {
3932 return ;
4033 }
4134 function maybeChild ( child ) {
42- if ( child && typeof child === 'object' && typeof child . type === 'string' ) {
35+ if ( typeof child ? .type === 'string' ) {
4336 c ( child ) ;
4437 return true ;
4538 }
@@ -93,22 +86,17 @@ function emptyOut(node) {
9386 node . type = 'EmptyStatement' ;
9487}
9588
96- function isUseStrict ( node ) {
97- return node . type === 'Literal' && node . value === 'use strict' ;
98- }
99-
10089function setLiteralValue ( item , value ) {
10190 item . value = value ;
102- item . raw = "'" + value + "'" ;
91+ item . raw = null ;
10392}
10493
10594function isLiteralString ( node ) {
106- return node . type === 'Literal' && ( node . raw [ 0 ] === '"' || node . raw [ 0 ] === "'" ) ;
95+ return node . type === 'Literal' && typeof node . value === 'string' ;
10796}
10897
109- function dump ( node , text ) {
110- if ( text ) print ( text ) ;
111- print ( JSON . stringify ( node , null , ' ' ) ) ;
98+ function dump ( node ) {
99+ console . log ( JSON . stringify ( node , null , ' ' ) ) ;
112100}
113101
114102// Mark inner scopes temporarily as empty statements. Returns
@@ -117,7 +105,7 @@ function ignoreInnerScopes(node) {
117105 const map = new WeakMap ( ) ;
118106 function ignore ( node ) {
119107 map . set ( node , node . type ) ;
120- node . type = 'EmptyStatement' ;
108+ emptyOut ( node ) ;
121109 }
122110 simpleWalk ( node , {
123111 FunctionDeclaration ( node ) {
@@ -151,13 +139,17 @@ function hasSideEffects(node) {
151139 let has = false ;
152140 fullWalk ( node , ( node ) => {
153141 switch ( node . type ) {
142+ case 'ExpressionStatement' :
143+ if ( node . directive ) {
144+ has = true ;
145+ }
146+ break ;
154147 // TODO: go through all the ESTree spec
155148 case 'Literal' :
156149 case 'Identifier' :
157150 case 'UnaryExpression' :
158151 case 'BinaryExpression' :
159152 case 'LogicalExpression' :
160- case 'ExpressionStatement' :
161153 case 'UpdateOperator' :
162154 case 'ConditionalExpression' :
163155 case 'FunctionDeclaration' :
@@ -284,7 +276,7 @@ function JSDCE(ast, aggressive) {
284276 }
285277 } ,
286278 ExpressionStatement ( node , _c ) {
287- if ( aggressive && ! hasSideEffects ( node ) && ! isUseStrict ( node . expression ) ) {
279+ if ( aggressive && ! hasSideEffects ( node ) ) {
288280 emptyOut ( node ) ;
289281 removed ++ ;
290282 }
@@ -954,7 +946,7 @@ function emitDCEGraph(ast) {
954946 info . reaches = sortedNamesFromMap ( info . reaches ) ;
955947 graph . push ( info ) ;
956948 } ) ;
957- print ( JSON . stringify ( graph , null , ' ' ) ) ;
949+ dump ( graph ) ;
958950}
959951
960952// Apply graph removals from running wasm-metadce. This only removes imports and
@@ -1042,31 +1034,21 @@ function applyDCEGraphRemovals(ast) {
10421034 }
10431035}
10441036
1045- // Need a parser to pass to acorn.Node constructor.
1046- // Create it once and reuse it.
1047- const stubParser = new acorn . Parser ( { ecmaVersion : 2021 } ) ;
1048-
1049- function createNode ( props ) {
1050- const node = new acorn . Node ( stubParser ) ;
1051- Object . assign ( node , props ) ;
1052- return node ;
1053- }
1054-
10551037function createLiteral ( value ) {
1056- return createNode ( {
1038+ return {
10571039 type : 'Literal' ,
10581040 value : value ,
10591041 raw : '' + value ,
1060- } ) ;
1042+ } ;
10611043}
10621044
10631045function makeCallExpression ( node , name , args ) {
10641046 Object . assign ( node , {
10651047 type : 'CallExpression' ,
1066- callee : createNode ( {
1048+ callee : {
10671049 type : 'Identifier' ,
10681050 name : name ,
1069- } ) ,
1051+ } ,
10701052 arguments : args ,
10711053 } ) ;
10721054}
@@ -1095,7 +1077,7 @@ function isEmscriptenHEAP(name) {
10951077// LE byte order for HEAP buffer
10961078function littleEndianHeap ( ast ) {
10971079 recursiveWalk ( ast , {
1098- FunctionDeclaration : ( node , c ) => {
1080+ FunctionDeclaration ( node , c ) {
10991081 // do not recurse into LE_HEAP_STORE, LE_HEAP_LOAD functions
11001082 if (
11011083 ! (
@@ -1106,13 +1088,13 @@ function littleEndianHeap(ast) {
11061088 c ( node . body ) ;
11071089 }
11081090 } ,
1109- VariableDeclarator : ( node , c ) => {
1091+ VariableDeclarator ( node , c ) {
11101092 if ( ! ( node . id . type === 'Identifier' && node . id . name . startsWith ( 'LE_ATOMICS_' ) ) ) {
11111093 c ( node . id ) ;
11121094 if ( node . init ) c ( node . init ) ;
11131095 }
11141096 } ,
1115- AssignmentExpression : ( node , c ) => {
1097+ AssignmentExpression ( node , c ) {
11161098 const target = node . left ;
11171099 const value = node . right ;
11181100 c ( value ) ;
@@ -1172,7 +1154,7 @@ function littleEndianHeap(ast) {
11721154 }
11731155 }
11741156 } ,
1175- CallExpression : ( node , c ) => {
1157+ CallExpression ( node , c ) {
11761158 if ( node . arguments ) {
11771159 for ( var a of node . arguments ) c ( a ) ;
11781160 }
@@ -1181,7 +1163,6 @@ function littleEndianHeap(ast) {
11811163 node . callee . type === 'MemberExpression' &&
11821164 node . callee . object . type === 'Identifier' &&
11831165 node . callee . object . name === 'Atomics' &&
1184- node . callee . property . type === 'Identifier' &&
11851166 ! node . callee . computed
11861167 ) {
11871168 makeCallExpression (
@@ -1193,7 +1174,7 @@ function littleEndianHeap(ast) {
11931174 c ( node . callee ) ;
11941175 }
11951176 } ,
1196- MemberExpression : ( node , c ) => {
1177+ MemberExpression ( node , c ) {
11971178 c ( node . property ) ;
11981179 if ( ! isHEAPAccess ( node ) ) {
11991180 // not accessing the HEAP
@@ -1269,39 +1250,37 @@ function growableHeap(ast) {
12691250 c ( node . body ) ;
12701251 }
12711252 } ,
1272- AssignmentExpression : ( node ) => {
1253+ AssignmentExpression ( node ) {
12731254 if ( node . left . type !== 'Identifier' ) {
12741255 // Don't transform `HEAPxx =` assignments.
12751256 growableHeap ( node . left ) ;
12761257 }
12771258 growableHeap ( node . right ) ;
12781259 } ,
1279- VariableDeclaration : ( node ) => {
1260+ VariableDeclarator ( node ) {
12801261 // Don't transform the var declarations for HEAP8 etc
1281- node . declarations . forEach ( ( decl ) => {
1282- // but do transform anything that sets a var to
1283- // something from HEAP8 etc
1284- if ( decl . init ) {
1285- growableHeap ( decl . init ) ;
1286- }
1287- } ) ;
1262+ // but do transform anything that sets a var to
1263+ // something from HEAP8 etc
1264+ if ( node . init ) {
1265+ growableHeap ( node . init ) ;
1266+ }
12881267 } ,
1289- Identifier : ( node ) => {
1268+ Identifier ( node ) {
12901269 if ( isEmscriptenHEAP ( node . name ) ) {
12911270 // Transform `HEAPxx` into `(growMemViews(), HEAPxx)`.
12921271 // Important: don't just do `growMemViews(HEAPxx)` because `growMemViews` reassigns `HEAPxx`
12931272 // and we want to get an updated value after that reassignment.
12941273 Object . assign ( node , {
12951274 type : 'SequenceExpression' ,
12961275 expressions : [
1297- createNode ( {
1276+ {
12981277 type : 'CallExpression' ,
1299- callee : createNode ( {
1278+ callee : {
13001279 type : 'Identifier' ,
13011280 name : 'growMemViews' ,
1302- } ) ,
1281+ } ,
13031282 arguments : [ ] ,
1304- } ) ,
1283+ } ,
13051284 { ...node } ,
13061285 ] ,
13071286 } ) ;
@@ -1337,12 +1316,7 @@ function unsignPointers(ast) {
13371316 right : {
13381317 type : 'Literal' ,
13391318 value : 0 ,
1340- raw : '0' ,
1341- start : 0 ,
1342- end : 0 ,
13431319 } ,
1344- start : 0 ,
1345- end : 0 ,
13461320 } ;
13471321 }
13481322
@@ -1357,7 +1331,6 @@ function unsignPointers(ast) {
13571331 node . callee . type === 'MemberExpression' &&
13581332 node . callee . object . type === 'Identifier' &&
13591333 isHeap ( node . callee . object . name ) &&
1360- node . callee . property . type === 'Identifier' &&
13611334 ! node . callee . computed
13621335 ) {
13631336 // This is a call on HEAP*.?. Specific things we need to fix up are
@@ -1430,12 +1403,12 @@ function asanify(ast) {
14301403}
14311404
14321405function multiply ( value , by ) {
1433- return createNode ( {
1406+ return {
14341407 type : 'BinaryExpression' ,
14351408 left : value ,
14361409 operator : '*' ,
14371410 right : createLiteral ( by ) ,
1438- } ) ;
1411+ } ;
14391412}
14401413
14411414// Replace direct heap access with SAFE_HEAP* calls.
@@ -1523,7 +1496,7 @@ function ensureMinifiedNames(n) {
15231496
15241497function minifyLocals ( ast ) {
15251498 // We are given a mapping of global names to their minified forms.
1526- assert ( extraInfo && extraInfo . globals ) ;
1499+ assert ( extraInfo ? .globals ) ;
15271500
15281501 for ( const fun of ast . body ) {
15291502 if ( fun . type !== 'FunctionDeclaration' ) {
@@ -1787,7 +1760,7 @@ function reattachComments(ast, commentsMap) {
17871760 // Collect all code symbols
17881761 ast . walk (
17891762 new terser . TreeWalker ( ( node ) => {
1790- if ( node . start && node . start . pos ) {
1763+ if ( node . start ? .pos ) {
17911764 symbols . push ( node ) ;
17921765 }
17931766 } ) ,
@@ -1853,11 +1826,6 @@ function trace(...args) {
18531826 }
18541827}
18551828
1856- function error ( ...args ) {
1857- console . error ( ...args ) ;
1858- throw new Error ( ...args ) ;
1859- }
1860-
18611829// If enabled, output retains parentheses and comments so that the
18621830// output can further be passed out to Closure.
18631831const closureFriendly = getArg ( '--closure-friendly' ) ;
@@ -1894,29 +1862,14 @@ if (closureFriendly) {
18941862 const currentComments = [ ] ;
18951863 Object . assign ( params , {
18961864 preserveParens : true ,
1897- onToken : ( token ) => {
1865+ onToken ( token ) {
18981866 // Associate comments with the start position of the next token.
18991867 sourceComments [ token . start ] = currentComments . slice ( ) ;
19001868 currentComments . length = 0 ;
19011869 } ,
19021870 onComment : currentComments ,
19031871 } ) ;
19041872}
1905- let ast ;
1906- try {
1907- ast = acorn . parse ( input , params ) ;
1908- } catch ( err ) {
1909- err . message += ( ( ) => {
1910- let errorMessage = '\n' + input . split ( acorn . lineBreak ) [ err . loc . line - 1 ] + '\n' ;
1911- let column = err . loc . column ;
1912- while ( column -- ) {
1913- errorMessage += ' ' ;
1914- }
1915- errorMessage += '^\n' ;
1916- return errorMessage ;
1917- } ) ( ) ;
1918- throw err ;
1919- }
19201873
19211874const registry = {
19221875 JSDCE ,
@@ -1934,13 +1887,23 @@ const registry = {
19341887 minifyGlobals,
19351888} ;
19361889
1937- passes . forEach ( ( pass ) => {
1938- trace ( `running AST pass: ${ pass } ` ) ;
1939- if ( ! ( pass in registry ) ) {
1940- error ( `unknown optimizer pass: ${ pass } ` ) ;
1890+ let ast ;
1891+ try {
1892+ ast = acorn . parse ( input , params ) ;
1893+ for ( let pass of passes ) {
1894+ const resolvedPass = registry [ pass ] ;
1895+ assert ( resolvedPass , `unknown optimizer pass: ${ pass } ` ) ;
1896+ resolvedPass ( ast ) ;
1897+ }
1898+ } catch ( err ) {
1899+ if ( err . loc ) {
1900+ err . message +=
1901+ '\n' +
1902+ `${ input . split ( acorn . lineBreak ) [ err . loc . line - 1 ] } \n` +
1903+ `${ ' ' . repeat ( err . loc . column ) } ^ ${ infile } :${ err . loc . line } :${ err . loc . column + 1 } ` ;
19411904 }
1942- registry [ pass ] ( ast ) ;
1943- } ) ;
1905+ throw err ;
1906+ }
19441907
19451908if ( ! noPrint ) {
19461909 const terserAst = terser . AST_Node . from_mozilla_ast ( ast ) ;
0 commit comments