Skip to content

Commit a327b5c

Browse files
trevoradecopybara-github
authored andcommitted
Update PolymerClassDefinition to collect methods from fields and properties assigned to functions.
PiperOrigin-RevId: 845461047
1 parent e8409c8 commit a327b5c

File tree

2 files changed

+95
-17
lines changed

2 files changed

+95
-17
lines changed

src/com/google/javascript/jscomp/PolymerClassDefinition.java

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -342,29 +342,20 @@ private static Node createDummyGoogModuleExportsTarget(AbstractCompiler compiler
342342

343343
JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(classNode);
344344

345-
JSDocInfo ctorInfo = null;
346345
Node constructor = NodeUtil.getEs6ClassConstructorMemberFunctionDef(classNode);
347-
if (constructor != null) {
348-
ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
349-
}
350346

351347
ImmutableList<MemberDefinition> properties =
352348
PolymerPassStaticUtils.extractProperties(
353349
propertiesDescriptor, DefinitionType.ES6Class, compiler, constructor);
354350

355-
List<MemberDefinition> methods = new ArrayList<>();
356-
for (Node keyNode = NodeUtil.getClassMembers(classNode).getFirstChild();
357-
keyNode != null;
358-
keyNode = keyNode.getNext()) {
359-
if (!keyNode.isMemberFunctionDef()) {
360-
continue;
361-
}
362-
methods.add(
363-
new MemberDefinition(
364-
NodeUtil.getBestJSDocInfo(keyNode),
365-
keyNode,
366-
keyNode.getFirstChild(),
367-
enclosingModule));
351+
List<MemberDefinition> methods = extractClassMethods(classNode, enclosingModule);
352+
353+
JSDocInfo ctorInfo = null;
354+
if (constructor != null) {
355+
ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
356+
357+
// Add properties assigned to functions in the constructor (i.e. `this.prop = function`).
358+
addFunctionAssignmentsFromConstructor(constructor, enclosingModule, methods);
368359
}
369360
CompilerInput input = compiler.getInput(NodeUtil.getEnclosingScript(classNode).getInputId());
370361

@@ -385,6 +376,56 @@ private static Node createDummyGoogModuleExportsTarget(AbstractCompiler compiler
385376
input);
386377
}
387378

379+
/** Extracts true class methods and fields assigned to functions from an ES6 class node. */
380+
private static List<MemberDefinition> extractClassMethods(Node classNode, Node enclosingModule) {
381+
List<MemberDefinition> methods = new ArrayList<>();
382+
for (Node keyNode = NodeUtil.getClassMembers(classNode).getFirstChild();
383+
keyNode != null;
384+
keyNode = keyNode.getNext()) {
385+
if (keyNode.isMemberFunctionDef() || isFunctionField(keyNode)) {
386+
methods.add(
387+
new MemberDefinition(
388+
NodeUtil.getBestJSDocInfo(keyNode),
389+
keyNode,
390+
keyNode.getFirstChild(),
391+
enclosingModule));
392+
}
393+
}
394+
return methods;
395+
}
396+
397+
/** Detects patterns like {@code field = function() {}} and {@code field = () => {})}. */
398+
private static boolean isFunctionField(Node field) {
399+
if (!field.isMemberFieldDef()) {
400+
return false;
401+
}
402+
Node value = field.getFirstChild();
403+
return value != null && value.isFunction();
404+
}
405+
406+
/**
407+
* Adds properties assigned to functions in the constructor body (e.g., {@code this.prop =
408+
* function()}) to the given list of methods.
409+
*/
410+
private static void addFunctionAssignmentsFromConstructor(
411+
Node constructor, Node enclosingModule, List<MemberDefinition> methods) {
412+
Node body = NodeUtil.getFunctionBody(constructor.getFirstChild());
413+
for (Node stmt = body.getFirstChild(); stmt != null; stmt = stmt.getNext()) {
414+
if (!stmt.isExprResult() || !stmt.getFirstChild().isAssign()) {
415+
continue;
416+
}
417+
418+
var assignment = stmt.getFirstChild();
419+
var propName = assignment.getFirstChild();
420+
var propValue = assignment.getLastChild();
421+
if (propName.isGetProp() && propName.getFirstChild().isThis() && propValue.isFunction()) {
422+
methods.add(
423+
new MemberDefinition(
424+
NodeUtil.getBestJSDocInfo(propName), propName, propValue, enclosingModule));
425+
}
426+
}
427+
}
428+
388429
/**
389430
* Appends a list of new MemberDefinitions to the end of a list and removes any previous
390431
* MemberDefinition in the list which has the same name as the new member.

test/com/google/javascript/jscomp/PolymerClassDefinitionTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
*/
1616
package com.google.javascript.jscomp;
1717

18+
import static com.google.common.collect.ImmutableList.toImmutableList;
1819
import static com.google.common.truth.Truth.assertThat;
1920
import static com.google.javascript.rhino.testing.NodeSubject.assertNode;
2021

22+
import com.google.common.collect.ImmutableList;
2123
import com.google.javascript.jscomp.modules.ModuleMap;
2224
import com.google.javascript.jscomp.modules.ModuleMetadataMap;
2325
import com.google.javascript.rhino.Node;
@@ -126,6 +128,41 @@ static get properties() {
126128
assertThat(def.props).hasSize(2);
127129
}
128130

131+
@Test
132+
public void testClassMethods() {
133+
PolymerClassDefinition def =
134+
parseAndExtractClassDefFromClass(
135+
"""
136+
class A extends Polymer.Element {
137+
static get is() { return 'x-element'; }
138+
method() {}
139+
/** @type {function()} */
140+
fieldWithFunction = function() {};
141+
fieldWithArrowFunction = () => {};
142+
fieldWithNumber = 1;
143+
fieldWithoutInitializer;
144+
constructor() {
145+
super();
146+
this.propertyWithFunction = function() {};
147+
this.propertyWithArrowFunction = () => {};
148+
this.propertyWithNumber = 2;
149+
}
150+
}
151+
""");
152+
153+
assertThat(def).isNotNull();
154+
ImmutableList<String> methodNames =
155+
def.methods.stream().map(m -> m.name.getString()).collect(toImmutableList());
156+
assertThat(methodNames)
157+
.containsExactly(
158+
"method",
159+
"fieldWithFunction",
160+
"fieldWithArrowFunction",
161+
"constructor",
162+
"propertyWithFunction",
163+
"propertyWithArrowFunction");
164+
}
165+
129166
@Test
130167
public void testDynamicDescriptor() {
131168
PolymerClassDefinition def =

0 commit comments

Comments
 (0)