@@ -68,6 +68,17 @@ impl Stats {
6868 }
6969}
7070
71+ #[ inline( always) ]
72+ fn is_child_identifier ( node : & Node , id : u16 ) -> bool {
73+ let mut cursor = node. object ( ) . walk ( ) ;
74+ for child in node. object ( ) . children ( & mut cursor) {
75+ if child. kind_id ( ) == id {
76+ return true ;
77+ }
78+ }
79+ false
80+ }
81+
7182#[ doc( hidden) ]
7283pub trait Nom
7384where
@@ -97,12 +108,41 @@ impl Nom for MozjsCode {
97108 use Mozjs :: * ;
98109
99110 match node. object ( ) . kind_id ( ) . into ( ) {
100- Function | FunctionDeclaration | MethodDefinition => {
111+ FunctionDeclaration | MethodDefinition => {
101112 stats. functions += 1 ;
102113 }
103- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
114+ GeneratorFunction | GeneratorFunctionDeclaration => {
104115 stats. closures += 1 ;
105116 }
117+ Function => {
118+ // Consider named functions as functions and unnamed ones as
119+ // closures.
120+ if count_specific_ancestors ! (
121+ node,
122+ VariableDeclarator | AssignmentExpression | LabeledStatement | Pair ,
123+ StatementBlock | ReturnStatement | NewExpression | Arguments
124+ ) > 0
125+ || is_child_identifier ( node, Identifier as u16 )
126+ {
127+ stats. functions += 1 ;
128+ } else {
129+ stats. closures += 1 ;
130+ }
131+ }
132+ ArrowFunction => {
133+ // Consider named functions as functions and unnamed ones as
134+ // closures.
135+ if count_specific_ancestors ! (
136+ node,
137+ VariableDeclarator | AssignmentExpression | LabeledStatement ,
138+ StatementBlock | ReturnStatement | NewExpression | CallExpression
139+ ) > 0
140+ {
141+ stats. functions += 1 ;
142+ } else {
143+ stats. closures += 1 ;
144+ }
145+ }
106146 _ => { }
107147 }
108148 }
@@ -113,12 +153,41 @@ impl Nom for JavascriptCode {
113153 use Javascript :: * ;
114154
115155 match node. object ( ) . kind_id ( ) . into ( ) {
116- Function | FunctionDeclaration | MethodDefinition => {
156+ FunctionDeclaration | MethodDefinition => {
117157 stats. functions += 1 ;
118158 }
119- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
159+ GeneratorFunction | GeneratorFunctionDeclaration => {
120160 stats. closures += 1 ;
121161 }
162+ Function => {
163+ // Consider named functions as functions and unnamed ones as
164+ // closures.
165+ if count_specific_ancestors ! (
166+ node,
167+ VariableDeclarator | AssignmentExpression | LabeledStatement | Pair ,
168+ StatementBlock | ReturnStatement | NewExpression | Arguments
169+ ) > 0
170+ || is_child_identifier ( node, Identifier as u16 )
171+ {
172+ stats. functions += 1 ;
173+ } else {
174+ stats. closures += 1 ;
175+ }
176+ }
177+ ArrowFunction => {
178+ // Consider named functions as functions and unnamed ones as
179+ // closures.
180+ if count_specific_ancestors ! (
181+ node,
182+ VariableDeclarator | AssignmentExpression | LabeledStatement ,
183+ StatementBlock | ReturnStatement | NewExpression | CallExpression
184+ ) > 0
185+ {
186+ stats. functions += 1 ;
187+ } else {
188+ stats. closures += 1 ;
189+ }
190+ }
122191 _ => { }
123192 }
124193 }
@@ -129,12 +198,41 @@ impl Nom for TypescriptCode {
129198 use Typescript :: * ;
130199
131200 match node. object ( ) . kind_id ( ) . into ( ) {
132- Function | FunctionDeclaration | MethodDefinition => {
201+ FunctionDeclaration | MethodDefinition => {
133202 stats. functions += 1 ;
134203 }
135- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
204+ GeneratorFunction | GeneratorFunctionDeclaration => {
136205 stats. closures += 1 ;
137206 }
207+ Function => {
208+ // Consider named functions as functions and unnamed ones as
209+ // closures.
210+ if count_specific_ancestors ! (
211+ node,
212+ VariableDeclarator | AssignmentExpression | LabeledStatement | Pair ,
213+ StatementBlock | ReturnStatement | NewExpression | Arguments
214+ ) > 0
215+ || is_child_identifier ( node, Identifier as u16 )
216+ {
217+ stats. functions += 1 ;
218+ } else {
219+ stats. closures += 1 ;
220+ }
221+ }
222+ ArrowFunction => {
223+ // Consider named functions as functions and unnamed ones as
224+ // closures.
225+ if count_specific_ancestors ! (
226+ node,
227+ VariableDeclarator | AssignmentExpression | LabeledStatement ,
228+ StatementBlock | ReturnStatement | NewExpression | CallExpression
229+ ) > 0
230+ {
231+ stats. functions += 1 ;
232+ } else {
233+ stats. closures += 1 ;
234+ }
235+ }
138236 _ => { }
139237 }
140238 }
@@ -145,12 +243,41 @@ impl Nom for TsxCode {
145243 use Tsx :: * ;
146244
147245 match node. object ( ) . kind_id ( ) . into ( ) {
148- Function | FunctionDeclaration | MethodDefinition => {
246+ FunctionDeclaration | MethodDefinition => {
149247 stats. functions += 1 ;
150248 }
151- GeneratorFunction | GeneratorFunctionDeclaration | ArrowFunction => {
249+ GeneratorFunction | GeneratorFunctionDeclaration => {
152250 stats. closures += 1 ;
153251 }
252+ Function => {
253+ // Consider named functions as functions and unnamed ones as
254+ // closures.
255+ if count_specific_ancestors ! (
256+ node,
257+ VariableDeclarator | AssignmentExpression | LabeledStatement | Pair ,
258+ StatementBlock | ReturnStatement | NewExpression | Arguments
259+ ) > 0
260+ || is_child_identifier ( node, Identifier as u16 )
261+ {
262+ stats. functions += 1 ;
263+ } else {
264+ stats. closures += 1 ;
265+ }
266+ }
267+ ArrowFunction => {
268+ // Consider named functions as functions and unnamed ones as
269+ // closures.
270+ if count_specific_ancestors ! (
271+ node,
272+ VariableDeclarator | AssignmentExpression | LabeledStatement ,
273+ StatementBlock | ReturnStatement | NewExpression | CallExpression
274+ ) > 0
275+ {
276+ stats. functions += 1 ;
277+ } else {
278+ stats. closures += 1 ;
279+ }
280+ }
154281 _ => { }
155282 }
156283 }
@@ -279,15 +406,72 @@ mod tests {
279406 }
280407
281408 #[ test]
282- fn c_nom ( ) {
409+ fn javascript_nom ( ) {
283410 check_metrics ! (
284- "int foo();
411+ "function f(a, b) {
412+ function foo(a) {
413+ return a;
414+ }
415+ var bar = (function () {
416+ var counter = 0;
417+ return function () {
418+ counter += 1;
419+ return counter
420+ }
421+ })();
422+ return bar(foo(a), a);
423+ }" ,
424+ "foo.js" ,
425+ JavascriptParser ,
426+ nom,
427+ [
428+ ( functions, 3 , usize ) , // f, foo, bar
429+ ( closures, 1 , usize ) , // return function ()
430+ ( total, 4 , usize )
431+ ]
432+ ) ;
433+ }
285434
286- int foo() {
287- return 0;
435+ #[ test]
436+ fn javascript_call_nom ( ) {
437+ check_metrics ! (
438+ "add_task(async function test_safe_mode() {
439+ gAppInfo.inSafeMode = true;
440+ });" ,
441+ "foo.js" ,
442+ JavascriptParser ,
443+ nom,
444+ [
445+ ( functions, 1 , usize ) , // test_safe_mode
446+ ( closures, 0 , usize ) ,
447+ ( total, 1 , usize )
448+ ]
449+ ) ;
450+ }
451+
452+ #[ test]
453+ fn javascript_assignment_nom ( ) {
454+ check_metrics ! (
455+ "AnimationTest.prototype.enableDisplay = function(element) {};" ,
456+ "foo.js" ,
457+ JavascriptParser ,
458+ nom,
459+ [
460+ ( functions, 1 , usize ) ,
461+ ( closures, 0 , usize ) ,
462+ ( total, 1 , usize )
463+ ]
464+ ) ;
465+ }
466+
467+ #[ test]
468+ fn javascript_labeled_nom ( ) {
469+ check_metrics ! (
470+ "toJSON: function() {
471+ return this.inspect(true);
288472 }" ,
289- "foo.c " ,
290- CppParser ,
473+ "foo.js " ,
474+ JavascriptParser ,
291475 nom,
292476 [
293477 ( functions, 1 , usize ) ,
@@ -296,4 +480,106 @@ mod tests {
296480 ]
297481 ) ;
298482 }
483+
484+ #[ test]
485+ fn javascript_pair_nom ( ) {
486+ check_metrics ! (
487+ "return {
488+ initialize: function(object) {
489+ this._object = object.toObject();
490+ },
491+ }" ,
492+ "foo.js" ,
493+ JavascriptParser ,
494+ nom,
495+ [
496+ ( functions, 1 , usize ) ,
497+ ( closures, 0 , usize ) ,
498+ ( total, 1 , usize )
499+ ]
500+ ) ;
501+ }
502+
503+ #[ test]
504+ fn javascript_unnamed_nom ( ) {
505+ check_metrics ! (
506+ "Ajax.getTransport = Try.these(
507+ function() {
508+ return function(){ return new XMLHttpRequest()}
509+ }
510+ );" ,
511+ "foo.js" ,
512+ JavascriptParser ,
513+ nom,
514+ [
515+ ( functions, 0 , usize ) ,
516+ ( closures, 2 , usize ) ,
517+ ( total, 2 , usize )
518+ ]
519+ ) ;
520+ }
521+
522+ #[ test]
523+ fn javascript_arrow_nom ( ) {
524+ check_metrics ! (
525+ "var materials = [\" Hydrogen\" ];
526+ materials.map(material => material.length);
527+ let add = (a, b) => a + b;" ,
528+ "foo.js" ,
529+ JavascriptParser ,
530+ nom,
531+ [
532+ ( functions, 1 , usize ) , // add
533+ ( closures, 1 , usize ) , // materials.map
534+ ( total, 2 , usize )
535+ ]
536+ ) ;
537+ }
538+
539+ #[ test]
540+ fn javascript_arrow_assignment_nom ( ) {
541+ check_metrics ! (
542+ "sink.onPull = () => { };" ,
543+ "foo.js" ,
544+ JavascriptParser ,
545+ nom,
546+ [
547+ ( functions, 1 , usize ) ,
548+ ( closures, 0 , usize ) ,
549+ ( total, 1 , usize )
550+ ]
551+ ) ;
552+ }
553+
554+ #[ test]
555+ fn javascript_arrow_new_nom ( ) {
556+ check_metrics ! (
557+ "const response = new Promise(resolve => channel.port1.onmessage = resolve);" ,
558+ "foo.js" ,
559+ JavascriptParser ,
560+ nom,
561+ [
562+ ( functions, 0 , usize ) ,
563+ ( closures, 1 , usize ) ,
564+ ( total, 1 , usize )
565+ ]
566+ ) ;
567+ }
568+
569+ #[ test]
570+ fn javascript_arrow_call_nom ( ) {
571+ check_metrics ! (
572+ "let notDisabled = TestUtils.waitForCondition(
573+ () => !backbutton.hasAttribute(\" disabled\" )
574+ );" ,
575+ "foo.js" ,
576+ JavascriptParser ,
577+ nom,
578+ [
579+ ( functions, 0 , usize ) ,
580+ ( closures, 1 , usize ) ,
581+ ( total, 1 , usize )
582+ ]
583+ ) ;
584+ }
299585}
0 commit comments