use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassRewriter method appendBehaviorPropertiesToBlock.
/**
* Iterates through all the behaviors of this polymer call, and appends the properties of each
* behavior to the given block.
*/
private void appendBehaviorPropertiesToBlock(PolymerClassDefinition cls, Node block, String basePath, boolean isExternsBlock) {
if (cls.behaviors == null || cls.behaviors.isEmpty() || cls.behaviorProps == null) {
return;
}
for (Map.Entry<MemberDefinition, BehaviorDefinition> itr : cls.behaviorProps.entrySet()) {
BehaviorDefinition behavior = itr.getValue();
MemberDefinition prop = itr.getKey();
Node propertyNode = getPropertyNode(prop, basePath);
if (propertyNode == null) {
continue;
}
JSTypeExpression propType = PolymerPassStaticUtils.getTypeFromProperty(prop, compiler);
if (propType == null) {
continue;
}
JSDocInfo info = null;
if (isExternsBlock) {
info = replaceJSDocAndAddNewVars(prop, propType, block);
} else {
JSDocInfo.Builder infoBuilder = getJSDocInfoBuilderForBehavior(behavior, prop);
infoBuilder.recordType(propType);
info = infoBuilder.build();
}
propertyNode.getFirstChild().setJSDocInfo(info);
block.addChildToBack(propertyNode);
}
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassDefinition method extractFromClassNode.
/**
* 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()).getFirstChild(); child != null; child = child.getNext()) {
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.getEs6ClassConstructorMemberFunctionDef(classNode);
if (constructor != null) {
ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
}
List<MemberDefinition> properties = PolymerPassStaticUtils.extractProperties(propertiesDescriptor, DefinitionType.ES6Class, compiler, constructor);
List<MemberDefinition> methods = new ArrayList<>();
for (Node keyNode = NodeUtil.getClassMembers(classNode).getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) {
if (!keyNode.isMemberFunctionDef()) {
continue;
}
methods.add(new MemberDefinition(NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild()));
}
return new PolymerClassDefinition(DefinitionType.ES6Class, classNode, target, /* hasGeneratedLhs= */
false, propertiesDescriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), null, properties, null, methods, null, null);
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassDefinition method extractFromCallNode.
/**
* Validates the class definition and if valid, destructively extracts the class definition from
* the AST.
*/
@Nullable
static PolymerClassDefinition extractFromCallNode(Node callNode, AbstractCompiler compiler, ModuleMetadata moduleMetadata, PolymerBehaviorExtractor behaviorExtractor) {
Node descriptor = NodeUtil.getArgumentForCallOrNew(callNode, 0);
if (descriptor == null || !descriptor.isObjectLit()) {
// report bad class definition
compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_DESCRIPTOR_NOT_VALID));
return null;
}
int paramCount = callNode.getChildCount() - 1;
if (paramCount != 1) {
compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_UNEXPECTED_PARAMS));
return null;
}
Node elName = NodeUtil.getFirstPropMatchingKey(descriptor, "is");
if (elName == null) {
compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_MISSING_IS));
return null;
}
boolean hasGeneratedLhs = false;
Node target;
if (NodeUtil.isNameDeclaration(callNode.getGrandparent())) {
target = IR.name(callNode.getParent().getString());
} else if (callNode.getParent().isAssign()) {
if (isGoogModuleExports(callNode.getParent())) {
// `exports = Polymer({` in a goog.module requires special handling, as adding a
// duplicate assignment to exports just confuses the compiler. Create a dummy declaration
// var exportsForPolymer$jscomp0 = Polymer({ // ...
target = createDummyGoogModuleExportsTarget(compiler, callNode);
} else {
target = callNode.getParent().getFirstChild().cloneTree();
}
} else {
String elNameStringBase = elName.isQualifiedName() ? elName.getQualifiedName().replace('.', '$') : elName.getString();
String elNameString = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, elNameStringBase);
elNameString += "Element";
target = IR.name(elNameString);
hasGeneratedLhs = true;
}
JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(target);
JSDocInfo ctorInfo = null;
Node constructor = NodeUtil.getFirstPropMatchingKey(descriptor, "factoryImpl");
if (constructor == null) {
constructor = NodeUtil.emptyFunction();
compiler.reportChangeToChangeScope(constructor);
constructor.srcrefTree(callNode);
} else {
ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
}
Node baseClass = NodeUtil.getFirstPropMatchingKey(descriptor, "extends");
String nativeBaseElement = baseClass == null ? null : baseClass.getString();
Node behaviorArray = NodeUtil.getFirstPropMatchingKey(descriptor, "behaviors");
ImmutableList<BehaviorDefinition> behaviors = behaviorExtractor.extractBehaviors(behaviorArray, moduleMetadata);
List<MemberDefinition> properties = new ArrayList<>();
Map<MemberDefinition, BehaviorDefinition> behaviorProps = new LinkedHashMap<>();
for (BehaviorDefinition behavior : behaviors) {
for (MemberDefinition prop : behavior.props) {
behaviorProps.put(prop, behavior);
}
}
overwriteMembersIfPresent(properties, PolymerPassStaticUtils.extractProperties(descriptor, DefinitionType.ObjectLiteral, compiler, /**
* constructor=
*/
null));
// Behaviors might get included multiple times for the same element. See test case
// testDuplicatedBehaviorsAreCopiedOnce
// In those cases, behaviorProps will have repeated names. We must remove those duplicates.
removeDuplicateBehaviorProps(behaviorProps);
// Remove behavior properties which are already present in element properties
removeBehaviorPropsOverlappingWithElementProps(behaviorProps, properties);
FeatureSet newFeatures = null;
if (!behaviors.isEmpty()) {
newFeatures = behaviors.get(0).features;
for (int i = 1; i < behaviors.size(); i++) {
newFeatures = newFeatures.union(behaviors.get(i).features);
}
}
List<MemberDefinition> methods = new ArrayList<>();
for (Node keyNode = descriptor.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) {
boolean isFunctionDefinition = keyNode.isMemberFunctionDef() || (keyNode.isStringKey() && keyNode.getFirstChild().isFunction());
if (isFunctionDefinition) {
methods.add(new MemberDefinition(NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild()));
}
}
return new PolymerClassDefinition(DefinitionType.ObjectLiteral, callNode, target, hasGeneratedLhs, descriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), nativeBaseElement, properties, behaviorProps, methods, behaviors, newFeatures);
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassDefinition method removeDuplicateBehaviorProps.
/**
* Removes duplicate properties from the given map, keeping only the first property
*
* <p>Duplicates occur when either the same behavior is transitively added multiple times to
* Polymer element, or when two unique added behaviors share a property with the same name.
*/
private static void removeDuplicateBehaviorProps(Map<MemberDefinition, BehaviorDefinition> behaviorProps) {
if (behaviorProps == null) {
return;
}
Iterator<Map.Entry<MemberDefinition, BehaviorDefinition>> behaviorsItr = behaviorProps.entrySet().iterator();
Set<String> seen = new HashSet<>();
while (behaviorsItr.hasNext()) {
MemberDefinition memberDefinition = behaviorsItr.next().getKey();
String propertyName = memberDefinition.name.getString();
if (!seen.add(propertyName)) {
behaviorsItr.remove();
}
}
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerBehaviorExtractor method getBehaviorFunctionsToCopy.
/**
* @return A list of functions from a behavior which should be copied to the element prototype.
*/
private static ImmutableList<MemberDefinition> getBehaviorFunctionsToCopy(Node behaviorObjLit) {
checkState(behaviorObjLit.isObjectLit());
ImmutableList.Builder<MemberDefinition> functionsToCopy = ImmutableList.builder();
for (Node keyNode = behaviorObjLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) {
boolean isFunctionDefinition = (keyNode.isStringKey() && keyNode.getFirstChild().isFunction()) || keyNode.isMemberFunctionDef();
if (isFunctionDefinition && !BEHAVIOR_NAMES_NOT_TO_COPY.contains(keyNode.getString())) {
functionsToCopy.add(new MemberDefinition(NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild()));
}
}
return functionsToCopy.build();
}
Aggregations