@@ -9,7 +9,7 @@ const UI5ClientConstants = require("../UI5ClientConstants");
9
9
const { findOwnProperty, getLocation, getPropertyKey, isMethodCall, isString} = require ( "../utils/ASTUtils" ) ;
10
10
const log = require ( "@ui5/logger" ) . getLogger ( "lbt:analyzer:JSModuleAnalyzer" ) ;
11
11
12
- // --------------------------------------------------------------------------------------------------------------------
12
+ // ------------------------------------------------------------------------------------------------------------------
13
13
14
14
const EnrichedVisitorKeys = ( function ( ) {
15
15
const VisitorKeys = require ( "estraverse" ) . VisitorKeys ;
@@ -24,17 +24,44 @@ const EnrichedVisitorKeys = (function() {
24
24
* E.g. in an IfExpression, the 'test' is always executed, whereas 'consequent'
25
25
* and 'alternate' are only executed under certain conditions.
26
26
*
27
- * The object is checked against the 'official' list of node types
28
- * and node keys as defined by 'estraverse' This helps to ensure that no new
29
- * syntax addition is missed and that the configured keys are valid.
27
+ * While visiting the AST of a JavaSCript file, the JSModuleAnalyzer uses this information
28
+ * to decide whether a code block is executed conditionally or unconditionally.
29
+ * Besides this information which is inherent to the language, the analyzer uses
30
+ * additional knowledge about special APIS / constructs (e.g. the factory function of
31
+ * an AMD module is known to be executed when the module is executed, an IIFE is known to
32
+ * be executed etc.)
33
+ *
34
+ * To be more robust against the evolution of the language, the object below is checked
35
+ * against the 'official' list of node types and node keys as defined by 'estraverse'.
36
+ * This helps to ensure that no new syntax addition is missed and that the configured
37
+ * keys are valid.
30
38
*/
31
39
const TempKeys = {
32
40
AssignmentExpression : [ ] ,
33
- AssignmentPattern : toBeDone ( [ "left" , "right" ] ) ,
41
+ /*
42
+ * function( >>>a=3<<<, b) {...}
43
+ * var [>>>a=3<<<, b] = [...];
44
+ *
45
+ * The default value expression (right) is only evaluated when there's no other value in
46
+ * the context of the pattern (e.g. destructuring or function call don't provide a value),
47
+ * so it's a conditional branch.
48
+ */
49
+ AssignmentPattern : [ "right" ] ,
34
50
ArrayExpression : [ ] ,
35
- ArrayPattern : toBeDone ( [ "elements" ] ) ,
51
+ /*
52
+ * var >>>[a=3, b]<<< = [...];
53
+ * All elements in an array pattern are unconditional.
54
+ */
55
+ ArrayPattern : [ ] , // elements
56
+ /*
57
+ * The body of an arrow function is only executed when the arrow function is executed
58
+ */
36
59
ArrowFunctionExpression : [ "body" ] ,
37
- AwaitExpression : toBeDone ( [ "argument" ] ) , // CAUTION: It's deferred to ES7.
60
+ /*
61
+ * The argument of await is always executed
62
+ * TODO how to handle code after the await expression?
63
+ */
64
+ AwaitExpression : [ ] , // argument
38
65
BlockStatement : [ ] ,
39
66
BinaryExpression : [ ] ,
40
67
BreakStatement : [ ] ,
@@ -49,12 +76,16 @@ const EnrichedVisitorKeys = (function() {
49
76
ContinueStatement : [ ] ,
50
77
DebuggerStatement : [ ] ,
51
78
DirectiveStatement : [ ] ,
52
- DoWhileStatement : [ ] , // condition is executed on the same conditions as the surrounding block, potentially repeated, block is always entered and might be repeated
79
+ /*
80
+ * 'condition' is executed on the same conditions as the surrounding block, potentially repeated,
81
+ * 'block' is always entered and might be repeated
82
+ */
83
+ DoWhileStatement : [ ] ,
53
84
EmptyStatement : [ ] ,
54
- ExportAllDeclaration : toBeDone ( [ "source" ] ) ,
55
- ExportDefaultDeclaration : toBeDone ( [ "declaration" ] ) ,
56
- ExportNamedDeclaration : toBeDone ( [ " declaration" , " specifiers" , " source" ] ) ,
57
- ExportSpecifier : toBeDone ( [ " exported" , " local" ] ) ,
85
+ ExportAllDeclaration : [ ] , // no parts of an export are conditional - source
86
+ ExportDefaultDeclaration : [ ] , // no parts of an export are conditional - declaration
87
+ ExportNamedDeclaration : [ ] , // no parts of an export are conditional - declaration, specifiers, source
88
+ ExportSpecifier : [ ] , // no parts of an export are conditional exported, local
58
89
ExpressionStatement : [ ] ,
59
90
ForStatement : [ "update" , "body" ] ,
60
91
ForInStatement : [ "body" ] ,
@@ -64,10 +95,22 @@ const EnrichedVisitorKeys = (function() {
64
95
GeneratorExpression : toBeDone ( [ "blocks" , "filter" , "body" ] ) , // CAUTION: It's deferred to ES7.
65
96
Identifier : [ ] ,
66
97
IfStatement : [ "consequent" , "alternate" ] ,
67
- ImportDeclaration : toBeDone ( [ "specifiers" , "source" ] ) ,
68
- ImportDefaultSpecifier : toBeDone ( [ "local" ] ) ,
69
- ImportNamespaceSpecifier : toBeDone ( [ "local" ] ) ,
70
- ImportSpecifier : toBeDone ( [ "imported" , "local" ] ) ,
98
+ /*
99
+ * all parts of an import declaration are executed unconditionally
100
+ */
101
+ ImportDeclaration : [ ] , // specifiers, source
102
+ /*
103
+ * import >>>a<<< from 'module';
104
+ */
105
+ ImportDefaultSpecifier : [ ] , // local
106
+ /*
107
+ * import >>>* as b<<< from 'module';
108
+ */
109
+ ImportNamespaceSpecifier : [ ] , // local
110
+ /*
111
+ * import {>>>a as c<<<,b} from 'module';
112
+ */
113
+ ImportSpecifier : [ ] , // imported, local
71
114
Literal : [ ] ,
72
115
LabeledStatement : [ ] ,
73
116
LogicalExpression : [ ] ,
@@ -77,27 +120,45 @@ const EnrichedVisitorKeys = (function() {
77
120
ModuleSpecifier : [ ] ,
78
121
NewExpression : [ ] ,
79
122
ObjectExpression : [ ] ,
80
- ObjectPattern : toBeDone ( [ "properties" ] ) ,
123
+ /*
124
+ * >>>{a,b,c}<<< = {...}
125
+ *
126
+ * All properties in an object pattern are executed.
127
+ */
128
+ ObjectPattern : [ ] , // properties
81
129
Program : [ ] ,
82
130
Property : [ ] ,
83
- RestElement : toBeDone ( [ "argument" ] ) ,
131
+ /*
132
+ * argument of the rest element is always executed under the same condition as the rest element itself
133
+ */
134
+ RestElement : [ ] , // argument
84
135
ReturnStatement : [ ] ,
85
136
SequenceExpression : [ ] ,
86
- SpreadElement : toBeDone ( [ "argument" ] ) ,
137
+ SpreadElement : [ ] , // the argument of the spread operator always needs to be evaluated - argument
87
138
Super : [ ] ,
88
139
SwitchStatement : [ ] ,
89
140
SwitchCase : [ "test" , "consequent" ] , // test and consequent are executed only conditionally
90
- TaggedTemplateExpression : toBeDone ( [ "tag" , "quasi" ] ) ,
141
+ /*
142
+ * all parts of a tagged template literal are executed under the same condition as the context
143
+ */
144
+ TaggedTemplateExpression : [ ] , // tag, quasi
91
145
TemplateElement : [ ] ,
92
- TemplateLiteral : toBeDone ( [ "quasis" , "expressions" ] ) ,
146
+ /*
147
+ * all parts of a template literal are executed under the same condition as the context
148
+ */
149
+ TemplateLiteral : [ ] , // quasis, expressions
93
150
ThisExpression : [ ] ,
94
151
ThrowStatement : [ ] ,
95
152
TryStatement : [ "handler" ] , // handler is called conditionally
96
153
UnaryExpression : [ ] ,
97
154
UpdateExpression : [ ] ,
98
155
VariableDeclaration : [ ] ,
99
156
VariableDeclarator : [ ] ,
100
- WhileStatement : [ "body" ] , // condition is executed on the same conditions as the surrounding block and potentially repeated, block maybe entered only conditionally but can be repeated
157
+ /*
158
+ * 'condition' is executed on the same conditions as the surrounding block and potentially repeated,
159
+ * 'block' maybe entered only conditionally but can be repeated
160
+ */
161
+ WhileStatement : [ "body" ] ,
101
162
WithStatement : [ ] ,
102
163
YieldExpression : [ ]
103
164
} ;
@@ -151,6 +212,9 @@ const CALL_JQUERY_SAP_IS_DECLARED = [["jQuery", "$"], "sap", "isDeclared"];
151
212
const CALL_JQUERY_SAP_REQUIRE = [ [ "jQuery" , "$" ] , "sap" , "require" ] ;
152
213
const CALL_JQUERY_SAP_REGISTER_PRELOADED_MODULES = [ [ "jQuery" , "$" ] , "sap" , "registerPreloadedModules" ] ;
153
214
215
+ function isCallableExpression ( node ) {
216
+ return node . type == Syntax . FunctionExpression || node . type == Syntax . ArrowFunctionExpression ;
217
+ }
154
218
155
219
/**
156
220
* Analyzes an already parsed JSDocument to collect information about the contained module(s).
@@ -233,7 +297,7 @@ class JSModuleAnalyzer {
233
297
if ( iArg < args . length && args [ iArg ] . type == Syntax . ArrayExpression ) {
234
298
iArg ++ ;
235
299
}
236
- if ( iArg < args . length && args [ iArg ] . type == Syntax . FunctionExpression ) {
300
+ if ( iArg < args . length && isCallableExpression ( args [ iArg ] ) ) {
237
301
// unconditionally execute the factory function
238
302
visit ( args [ iArg ] . body , conditional ) ;
239
303
}
@@ -256,7 +320,7 @@ class JSModuleAnalyzer {
256
320
analyzeDependencyArray ( args [ iArg ] . elements , conditional , null ) ;
257
321
iArg ++ ;
258
322
}
259
- if ( iArg < args . length && args [ iArg ] . type == Syntax . FunctionExpression ) {
323
+ if ( iArg < args . length && isCallableExpression ( args [ iArg ] ) ) {
260
324
// analyze the callback function
261
325
visit ( args [ iArg ] . body , conditional ) ;
262
326
}
@@ -275,7 +339,7 @@ class JSModuleAnalyzer {
275
339
let legacyCall = isMethodCall ( node , CALL_JQUERY_SAP_REGISTER_PRELOADED_MODULES ) ;
276
340
info . setFormat ( legacyCall ? ModuleFormat . UI5_LEGACY : ModuleFormat . UI5_DEFINE ) ;
277
341
onRegisterPreloadedModules ( node , legacyCall ) ;
278
- } else if ( node . callee . type === Syntax . FunctionExpression ) {
342
+ } else if ( isCallableExpression ( node . callee ) ) {
279
343
// recognizes a scope function declaration + argument
280
344
visit ( node . arguments , conditional ) ;
281
345
// NODE-TODO defaults of callee?
0 commit comments