Skip to content

Commit

Permalink
Reworks the polymer passes to support version 2 and proper advanced m…
Browse files Browse the repository at this point in the history
…ode renaming.

When the --polymer_version=2 flag is specified, renaming behavior changes from version 1.
Declared properties are renamed unless they are quoted, read only, or reflected to an attribute.
Adds new annotations from the polymer-analyzer.
Auto-injects a property reflection polyfill so that declared properties in advanced mode are renamed consistently.

Closes #2338

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=161701673
  • Loading branch information
ChadKillingsworth authored and Tyler Breisacher committed Jul 12, 2017
1 parent 055932c commit 635406e
Show file tree
Hide file tree
Showing 29 changed files with 2,314 additions and 309 deletions.
5 changes: 3 additions & 2 deletions src/com/google/javascript/jscomp/ClosureCodingConvention.java
Expand Up @@ -381,8 +381,9 @@ public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
} }


Node callName = callNode.getFirstChild(); Node callName = callNode.getFirstChild();
if (!callName.matchesQualifiedName("goog.reflect.object") || if (!(callName.matchesQualifiedName("goog.reflect.object")
callNode.getChildCount() != 3) { || callName.matchesQualifiedName("$jscomp.reflectObject"))
|| callNode.getChildCount() != 3) {
return null; return null;
} }


Expand Down
5 changes: 4 additions & 1 deletion src/com/google/javascript/jscomp/DefaultPassConfig.java
Expand Up @@ -3094,7 +3094,10 @@ static Map<String, Node> getAdditionalReplacements(
new HotSwapPassFactory("polymerPass", true) { new HotSwapPassFactory("polymerPass", true) {
@Override @Override
protected HotSwapCompilerPass create(AbstractCompiler compiler) { protected HotSwapCompilerPass create(AbstractCompiler compiler) {
return new PolymerPass(compiler); return new PolymerPass(
compiler,
compiler.getOptions().polymerVersion,
compiler.getOptions().propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED);
} }


@Override @Override
Expand Down
24 changes: 24 additions & 0 deletions src/com/google/javascript/jscomp/JSDocInfoPrinter.java
Expand Up @@ -69,6 +69,9 @@ public String print(JSDocInfo info) {
// implicitCast // implicitCast
// suppress // suppress
// deprecated // deprecated
// polymer
// polymerBehavior
// mixinFunction
parts.add("/**"); parts.add("/**");


if (info.isExport()) { if (info.isExport()) {
Expand Down Expand Up @@ -221,6 +224,27 @@ public String print(JSDocInfo info) {
multiline = true; multiline = true;
} }


if (info.isPolymer()) {
multiline = true;
parts.add("@polymer");
}
if (info.isPolymerBehavior()) {
multiline = true;
parts.add("@polymerBehavior");
}
if (info.isMixinFunction()) {
multiline = true;
parts.add("@mixinFunction");
}
if (info.isMixinClass()) {
multiline = true;
parts.add("@mixinClass");
}
if (info.isCustomElement()) {
multiline = true;
parts.add("@customElement");
}

parts.add("*/"); parts.add("*/");


StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Expand Down
30 changes: 26 additions & 4 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -2231,9 +2231,31 @@ static Node getFirstPropMatchingKey(Node n, String keyName) {
return null; return null;
} }


/** /** @return The first getter in the class members that matches the key. */
* @return The first computed property in the objlit whose key matches {@code key}. @Nullable
*/ static Node getFirstGetterMatchingKey(Node n, String keyName) {
Preconditions.checkState(n.isClassMembers() || n.isObjectLit());
for (Node keyNode : n.children()) {
if (keyNode.isGetterDef() && keyNode.getString().equals(keyName)) {
return keyNode;
}
}
return null;
}

/** @return The first setter in the class members that matches the key. */
@Nullable
static Node getFirstSetterMatchingKey(Node n, String keyName) {
Preconditions.checkState(n.isClassMembers() || n.isObjectLit());
for (Node keyNode : n.children()) {
if (keyNode.isSetterDef() && keyNode.getString().equals(keyName)) {
return keyNode;
}
}
return null;
}

/** @return The first computed property in the objlit whose key matches {@code key}. */
@Nullable @Nullable
static Node getFirstComputedPropMatchingKey(Node objlit, Node key) { static Node getFirstComputedPropMatchingKey(Node objlit, Node key) {
checkState(objlit.isObjectLit()); checkState(objlit.isObjectLit());
Expand Down Expand Up @@ -2577,7 +2599,7 @@ public boolean apply(Node n) {
} }
}; };


private static final Set<Token> DEFINITE_CFG_ROOTS = private static final ImmutableSet<Token> DEFINITE_CFG_ROOTS =
ImmutableSet.of(Token.FUNCTION, Token.SCRIPT, Token.MODULE_BODY, Token.ROOT); ImmutableSet.of(Token.FUNCTION, Token.SCRIPT, Token.MODULE_BODY, Token.ROOT);


static boolean isValidCfgRoot(Node n) { static boolean isValidCfgRoot(Node n) {
Expand Down
10 changes: 6 additions & 4 deletions src/com/google/javascript/jscomp/PolymerBehaviorExtractor.java
Expand Up @@ -66,10 +66,11 @@ ImmutableList<BehaviorDefinition> extractBehaviors(Node behaviorArray) {
for (Node behaviorName : behaviorArray.children()) { for (Node behaviorName : behaviorArray.children()) {
if (behaviorName.isObjectLit()) { if (behaviorName.isObjectLit()) {
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(behaviorName, compiler); PolymerPassStaticUtils.switchDollarSignPropsToBrackets(behaviorName, compiler);
PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(behaviorName); PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(behaviorName, compiler);
behaviors.add( behaviors.add(
new BehaviorDefinition( new BehaviorDefinition(
PolymerPassStaticUtils.extractProperties(behaviorName, compiler), PolymerPassStaticUtils.extractProperties(
behaviorName, PolymerClassDefinition.DefinitionType.ObjectLiteral, compiler),
getBehaviorFunctionsToCopy(behaviorName), getBehaviorFunctionsToCopy(behaviorName),
getNonPropertyMembersToCopy(behaviorName), getNonPropertyMembersToCopy(behaviorName),
!NodeUtil.isInFunction(behaviorName), !NodeUtil.isInFunction(behaviorName),
Expand Down Expand Up @@ -119,10 +120,11 @@ ImmutableList<BehaviorDefinition> extractBehaviors(Node behaviorArray) {
behaviors.addAll(extractBehaviors(behaviorValue)); behaviors.addAll(extractBehaviors(behaviorValue));
} else if (behaviorValue.isObjectLit()) { } else if (behaviorValue.isObjectLit()) {
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(behaviorValue, compiler); PolymerPassStaticUtils.switchDollarSignPropsToBrackets(behaviorValue, compiler);
PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(behaviorValue); PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(behaviorValue, compiler);
behaviors.add( behaviors.add(
new BehaviorDefinition( new BehaviorDefinition(
PolymerPassStaticUtils.extractProperties(behaviorValue, compiler), PolymerPassStaticUtils.extractProperties(
behaviorValue, PolymerClassDefinition.DefinitionType.ObjectLiteral, compiler),
getBehaviorFunctionsToCopy(behaviorValue), getBehaviorFunctionsToCopy(behaviorValue),
getNonPropertyMembersToCopy(behaviorValue), getNonPropertyMembersToCopy(behaviorValue),
isGlobalDeclaration, isGlobalDeclaration,
Expand Down
102 changes: 97 additions & 5 deletions src/com/google/javascript/jscomp/PolymerClassDefinition.java
Expand Up @@ -34,6 +34,16 @@
* class. * class.
*/ */
final class PolymerClassDefinition { final class PolymerClassDefinition {
static enum DefinitionType {
ObjectLiteral,
ES6Class
}

/** The declaration style used for the Polymer definition */
final DefinitionType defType;

/** The Polymer call or class node which defines the Element. */
final Node definition;


/** The target node (LHS) for the Polymer element definition. */ /** The target node (LHS) for the Polymer element definition. */
final Node target; final Node target;
Expand All @@ -45,18 +55,20 @@ final class PolymerClassDefinition {
final MemberDefinition constructor; final MemberDefinition constructor;


/** The name of the native HTML element which this element extends. */ /** The name of the native HTML element which this element extends. */
final String nativeBaseElement; @Nullable final String nativeBaseElement;


/** Properties declared in the Polymer "properties" block. */ /** Properties declared in the Polymer "properties" block. */
final List<MemberDefinition> props; final List<MemberDefinition> props;


/** Flattened list of behavior definitions used by this element. */ /** Flattened list of behavior definitions used by this element. */
final ImmutableList<BehaviorDefinition> behaviors; @Nullable final ImmutableList<BehaviorDefinition> behaviors;


/** Language features that should be carried over to the extraction destination. */ /** Language features that should be carried over to the extraction destination. */
final FeatureSet features; @Nullable final FeatureSet features;


PolymerClassDefinition( PolymerClassDefinition(
DefinitionType defType,
Node definition,
Node target, Node target,
Node descriptor, Node descriptor,
JSDocInfo classInfo, JSDocInfo classInfo,
Expand All @@ -65,8 +77,10 @@ final class PolymerClassDefinition {
List<MemberDefinition> props, List<MemberDefinition> props,
ImmutableList<BehaviorDefinition> behaviors, ImmutableList<BehaviorDefinition> behaviors,
FeatureSet features) { FeatureSet features) {
this.defType = defType;
this.definition = definition;
this.target = target; this.target = target;
checkState(descriptor.isObjectLit()); checkState(descriptor == null || descriptor.isObjectLit());
this.descriptor = descriptor; this.descriptor = descriptor;
this.constructor = constructor; this.constructor = constructor;
this.nativeBaseElement = nativeBaseElement; this.nativeBaseElement = nativeBaseElement;
Expand Down Expand Up @@ -139,7 +153,9 @@ final class PolymerClassDefinition {
overwriteMembersIfPresent(allProperties, behavior.props); overwriteMembersIfPresent(allProperties, behavior.props);
} }
overwriteMembersIfPresent( overwriteMembersIfPresent(
allProperties, PolymerPassStaticUtils.extractProperties(descriptor, compiler)); allProperties,
PolymerPassStaticUtils.extractProperties(
descriptor, DefinitionType.ObjectLiteral, compiler));


FeatureSet newFeatures = null; FeatureSet newFeatures = null;
if (!behaviors.isEmpty()) { if (!behaviors.isEmpty()) {
Expand All @@ -150,6 +166,8 @@ final class PolymerClassDefinition {
} }


return new PolymerClassDefinition( return new PolymerClassDefinition(
DefinitionType.ObjectLiteral,
callNode,
target, target,
descriptor, descriptor,
classInfo, classInfo,
Expand All @@ -160,6 +178,80 @@ final class PolymerClassDefinition {
newFeatures); newFeatures);
} }


/**
* Validates the class definition and if valid, extracts the class definition from the AST. As
* opposed to the Polymer 1 extraction, this operation is non-destructive.
*/
@Nullable
static PolymerClassDefinition extractFromClassNode(
Node classNode, AbstractCompiler compiler, GlobalNamespace globalNames) {
checkState(classNode != null && classNode.isClass());

// The supported case is for the config getter to return an object literal descriptor.
Node propertiesDescriptor = null;
Node propertiesGetter =
NodeUtil.getFirstGetterMatchingKey(NodeUtil.getClassMembers(classNode), "properties");
if (propertiesGetter != null) {
if (!propertiesGetter.isStaticMember()) {
// report bad class definition
compiler.report(
JSError.make(classNode, PolymerPassErrors.POLYMER_CLASS_PROPERTIES_NOT_STATIC));
} else {
for (Node child : NodeUtil.getFunctionBody(propertiesGetter.getFirstChild()).children()) {
if (child.isReturn()) {
if (child.hasChildren() && child.getFirstChild().isObjectLit()) {
propertiesDescriptor = child.getFirstChild();
break;
} else {
compiler.report(
JSError.make(
propertiesGetter, PolymerPassErrors.POLYMER_CLASS_PROPERTIES_INVALID));
}
}
}
}
}

Node target;
if (NodeUtil.isNameDeclaration(classNode.getGrandparent())) {
target = IR.name(classNode.getParent().getString());
} else if (classNode.getParent().isAssign()
&& classNode.getParent().getFirstChild().isQualifiedName()) {
target = classNode.getParent().getFirstChild();
} else if (!classNode.getFirstChild().isEmpty()) {
target = classNode.getFirstChild();
} else {
// issue error - no name found
compiler.report(JSError.make(classNode, PolymerPassErrors.POLYMER_CLASS_UNNAMED));
return null;
}

JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(classNode);

JSDocInfo ctorInfo = null;
Node constructor =
NodeUtil.getFirstPropMatchingKey(NodeUtil.getClassMembers(classNode), "constructor");
if (constructor != null) {
ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
}

List<MemberDefinition> allProperties =
PolymerPassStaticUtils.extractProperties(
propertiesDescriptor, DefinitionType.ES6Class, compiler);

return new PolymerClassDefinition(
DefinitionType.ES6Class,
classNode,
target,
propertiesDescriptor,
classInfo,
new MemberDefinition(ctorInfo, null, constructor),
null,
allProperties,
null,
null);
}

/** /**
* Appends a list of new MemberDefinitions to the end of a list and removes any previous * Appends a list of new MemberDefinitions to the end of a list and removes any previous
* MemberDefinition in the list which has the same name as the new member. * MemberDefinition in the list which has the same name as the new member.
Expand Down

0 comments on commit 635406e

Please sign in to comment.