@@ -60,13 +60,13 @@ function simpleWalk(node, cs) {
60
60
}
61
61
62
62
// Full post-order walk, calling a single function for all types. If |pre| is
63
- // provided, it is called in pre-order (before children).
63
+ // provided, it is called in pre-order (before children). If |pre| returns
64
+ // `false`, the node and its children will be skipped.
64
65
function fullWalk ( node , c , pre ) {
65
- if ( pre ) {
66
- pre ( node ) ;
66
+ if ( pre ?. ( node ) !== false ) {
67
+ visitChildren ( node , ( child ) => fullWalk ( child , c , pre ) ) ;
68
+ c ( node ) ;
67
69
}
68
- visitChildren ( node , ( child ) => fullWalk ( child , c , pre ) ) ;
69
- c ( node ) ;
70
70
}
71
71
72
72
// Recursive post-order walk, calling properties on an object by node type,
@@ -100,111 +100,78 @@ function dump(node) {
100
100
console . log ( JSON . stringify ( node , null , ' ' ) ) ;
101
101
}
102
102
103
- // Mark inner scopes temporarily as empty statements. Returns
104
- // a special object that must be used to restore them.
105
- function ignoreInnerScopes ( node ) {
106
- const map = new WeakMap ( ) ;
107
- function ignore ( node ) {
108
- map . set ( node , node . type ) ;
109
- emptyOut ( node ) ;
110
- }
111
- simpleWalk ( node , {
112
- FunctionDeclaration ( node ) {
113
- ignore ( node ) ;
114
- } ,
115
- FunctionExpression ( node ) {
116
- ignore ( node ) ;
117
- } ,
118
- ArrowFunctionExpression ( node ) {
119
- ignore ( node ) ;
120
- } ,
121
- // TODO: arrow etc.
122
- } ) ;
123
- return map ;
124
- }
125
-
126
- // Mark inner scopes temporarily as empty statements.
127
- function restoreInnerScopes ( node , map ) {
128
- fullWalk ( node , ( node ) => {
129
- if ( map . has ( node ) ) {
130
- node . type = map . get ( node ) ;
131
- map . delete ( node ) ;
132
- restoreInnerScopes ( node , map ) ;
133
- }
134
- } ) ;
135
- }
136
-
137
103
function hasSideEffects ( node ) {
138
104
// Conservative analysis.
139
- const map = ignoreInnerScopes ( node ) ;
140
105
let has = false ;
141
- fullWalk ( node , ( node ) => {
142
- switch ( node . type ) {
143
- case 'ExpressionStatement' :
144
- if ( node . directive ) {
145
- has = true ;
106
+ fullWalk (
107
+ node ,
108
+ ( node ) => {
109
+ switch ( node . type ) {
110
+ case 'ExpressionStatement' :
111
+ if ( node . directive ) {
112
+ has = true ;
113
+ }
114
+ break ;
115
+ // TODO: go through all the ESTree spec
116
+ case 'Literal' :
117
+ case 'Identifier' :
118
+ case 'UnaryExpression' :
119
+ case 'BinaryExpression' :
120
+ case 'LogicalExpression' :
121
+ case 'UpdateOperator' :
122
+ case 'ConditionalExpression' :
123
+ case 'VariableDeclaration' :
124
+ case 'VariableDeclarator' :
125
+ case 'ObjectExpression' :
126
+ case 'Property' :
127
+ case 'SpreadElement' :
128
+ case 'BlockStatement' :
129
+ case 'ArrayExpression' :
130
+ case 'EmptyStatement' : {
131
+ break ; // safe
146
132
}
147
- break ;
148
- // TODO: go through all the ESTree spec
149
- case 'Literal' :
150
- case 'Identifier' :
151
- case 'UnaryExpression' :
152
- case 'BinaryExpression' :
153
- case 'LogicalExpression' :
154
- case 'UpdateOperator' :
155
- case 'ConditionalExpression' :
156
- case 'FunctionDeclaration' :
157
- case 'FunctionExpression' :
158
- case 'ArrowFunctionExpression' :
159
- case 'VariableDeclaration' :
160
- case 'VariableDeclarator' :
161
- case 'ObjectExpression' :
162
- case 'Property' :
163
- case 'SpreadElement' :
164
- case 'BlockStatement' :
165
- case 'ArrayExpression' :
166
- case 'EmptyStatement' : {
167
- break ; // safe
168
- }
169
- case 'MemberExpression' : {
170
- // safe if on Math (or other familiar objects, TODO)
171
- if ( node . object . type !== 'Identifier' || node . object . name !== 'Math' ) {
172
- // console.error('because member on ' + node.object.name);
173
- has = true ;
133
+ case 'MemberExpression' : {
134
+ // safe if on Math (or other familiar objects, TODO)
135
+ if ( node . object . type !== 'Identifier' || node . object . name !== 'Math' ) {
136
+ // console.error('because member on ' + node.object.name);
137
+ has = true ;
138
+ }
139
+ break ;
174
140
}
175
- break ;
176
- }
177
- case 'NewExpression' : {
178
- // default to unsafe, but can be safe on some familiar objects
179
- if ( node . callee . type === 'Identifier' ) {
180
- const name = node . callee . name ;
181
- if (
182
- name === 'TextDecoder' ||
183
- name === 'ArrayBuffer' ||
184
- name === 'Int8Array' ||
185
- name === 'Uint8Array' ||
186
- name === 'Int16Array' ||
187
- name === 'Uint16Array' ||
188
- name === 'Int32Array' ||
189
- name === 'Uint32Array' ||
190
- name === 'Float32Array' ||
191
- name === 'Float64Array'
192
- ) {
193
- // no side effects, but the arguments might (we walk them in
194
- // full walk as well)
195
- break ;
141
+ case 'NewExpression' : {
142
+ // default to unsafe, but can be safe on some familiar objects
143
+ if ( node . callee . type === 'Identifier' ) {
144
+ const name = node . callee . name ;
145
+ if (
146
+ name === 'TextDecoder' ||
147
+ name === 'ArrayBuffer' ||
148
+ name === 'Int8Array' ||
149
+ name === 'Uint8Array' ||
150
+ name === 'Int16Array' ||
151
+ name === 'Uint16Array' ||
152
+ name === 'Int32Array' ||
153
+ name === 'Uint32Array' ||
154
+ name === 'Float32Array' ||
155
+ name === 'Float64Array'
156
+ ) {
157
+ // no side effects, but the arguments might (we walk them in
158
+ // full walk as well)
159
+ break ;
160
+ }
196
161
}
162
+ // not one of the safe cases
163
+ has = true ;
164
+ break ;
165
+ }
166
+ default : {
167
+ has = true ;
197
168
}
198
- // not one of the safe cases
199
- has = true ;
200
- break ;
201
- }
202
- default : {
203
- has = true ;
204
169
}
205
- }
206
- } ) ;
207
- restoreInnerScopes ( node , map ) ;
170
+ } ,
171
+ ( node ) =>
172
+ // Ignore inner scopes.
173
+ ! [ 'FunctionDeclaration' , 'FunctionExpression' , 'ArrowFunctionExpression' ] . includes ( node . type ) ,
174
+ ) ;
208
175
return has ;
209
176
}
210
177
0 commit comments