Skip to content

Commit d9c920b

Browse files
committed
Fix Javascript Nom
1 parent 1bae86a commit d9c920b

File tree

1 file changed

+300
-14
lines changed

1 file changed

+300
-14
lines changed

src/metrics/nom.rs

Lines changed: 300 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
7283
pub trait Nom
7384
where
@@ -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

Comments
 (0)