use of com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition in project closure-compiler by google.
the class PolymerClassRewriter method parseReadOnlyProperties.
/**
* Generates the _set* setters for readonly properties and appends them to the given block.
*
* @return A List of all readonly properties.
*/
private ImmutableList<MemberDefinition> parseReadOnlyProperties(final PolymerClassDefinition cls, Node block) {
String qualifiedPath = cls.target.getQualifiedName() + ".prototype.";
ImmutableList.Builder<MemberDefinition> readOnlyProps = ImmutableList.builder();
for (MemberDefinition prop : cls.props) {
// Generate the setter for readOnly properties.
if (prop.value.isObjectLit()) {
Node readOnlyValue = NodeUtil.getFirstPropMatchingKey(prop.value, "readOnly");
if (readOnlyValue != null && readOnlyValue.isTrue()) {
Node setter = makeReadOnlySetter(prop, qualifiedPath);
setter.srcrefTreeIfMissing(prop.name);
block.addChildToBack(setter);
readOnlyProps.add(prop);
}
}
}
if (cls.behaviorProps != null) {
for (Map.Entry<MemberDefinition, BehaviorDefinition> itr : cls.behaviorProps.entrySet()) {
// Generate the setter for readOnly properties.
MemberDefinition prop = itr.getKey();
if (prop.value.isObjectLit()) {
Node readOnlyValue = NodeUtil.getFirstPropMatchingKey(prop.value, "readOnly");
if (readOnlyValue != null && readOnlyValue.isTrue()) {
Node setter = makeReadOnlySetter(prop, qualifiedPath);
setter.srcrefTreeIfMissing(prop.name);
block.addChildToBack(setter);
readOnlyProps.add(prop);
}
}
}
}
return readOnlyProps.build();
}
use of com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition 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.PolymerBehaviorExtractor.BehaviorDefinition 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.PolymerBehaviorExtractor.BehaviorDefinition 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, GlobalNamespace globalNames) {
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;
}
Node target;
if (NodeUtil.isNameDeclaration(callNode.getGrandparent())) {
target = IR.name(callNode.getParent().getString());
} else if (callNode.getParent().isAssign()) {
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);
}
JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(target);
JSDocInfo ctorInfo = null;
Node constructor = NodeUtil.getFirstPropMatchingKey(descriptor, "factoryImpl");
if (constructor == null) {
constructor = NodeUtil.emptyFunction();
compiler.reportChangeToChangeScope(constructor);
constructor.useSourceInfoFromForTree(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");
PolymerBehaviorExtractor behaviorExtractor = new PolymerBehaviorExtractor(compiler, globalNames);
ImmutableList<BehaviorDefinition> behaviors = behaviorExtractor.extractBehaviors(behaviorArray);
List<MemberDefinition> allProperties = new ArrayList<>();
for (BehaviorDefinition behavior : behaviors) {
overwriteMembersIfPresent(allProperties, behavior.props);
}
overwriteMembersIfPresent(allProperties, PolymerPassStaticUtils.extractProperties(descriptor, DefinitionType.ObjectLiteral, compiler));
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);
}
}
return new PolymerClassDefinition(DefinitionType.ObjectLiteral, callNode, target, descriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), nativeBaseElement, allProperties, behaviors, newFeatures);
}
use of com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition in project closure-compiler by google.
the class PolymerClassRewriter method appendBehaviorMembersToBlock.
/**
* Appends all required behavior functions and non-property members to the given block.
*/
private void appendBehaviorMembersToBlock(final PolymerClassDefinition cls, Node block) {
String qualifiedPath = cls.target.getQualifiedName() + ".prototype.";
Map<String, Node> nameToExprResult = new HashMap<>();
for (BehaviorDefinition behavior : cls.behaviors) {
for (MemberDefinition behaviorFunction : behavior.functionsToCopy) {
String fnName = behaviorFunction.name.getString();
// Don't copy functions already defined by the element itself.
if (NodeUtil.getFirstPropMatchingKey(cls.descriptor, fnName) != null) {
continue;
}
// Avoid copying over the same function twice. The last definition always wins.
if (nameToExprResult.containsKey(fnName)) {
nameToExprResult.get(fnName).detach();
}
Node fnValue = behaviorFunction.value.cloneTree();
NodeUtil.markNewScopesChanged(fnValue, compiler);
Node exprResult = IR.exprResult(IR.assign(NodeUtil.newQName(compiler, qualifiedPath + fnName), fnValue));
exprResult.srcrefTreeIfMissing(behaviorFunction.name);
JSDocInfo.Builder info = getJSDocInfoBuilderForBehavior(behavior, behaviorFunction);
// Uses of private members that come from behaviors are not recognized correctly,
// so just suppress that warning.
info.recordSuppression("unusedPrivateMembers");
// making the type system understand that methods are "inherited" from behaviors.
if (behaviorFunction.info != null && behaviorFunction.info.getVisibility() == Visibility.PROTECTED) {
info.overwriteVisibility(Visibility.PUBLIC);
}
// symbols which do not exist in the element's scope. Only copy a function stub.
if (!behavior.isGlobalDeclaration) {
Node body = NodeUtil.getFunctionBody(fnValue);
if (fnValue.isArrowFunction() && !NodeUtil.getFunctionBody(fnValue).isBlock()) {
// replace `() => <someExpr>` with `() => undefined`
body.replaceWith(NodeUtil.newUndefinedNode(body));
} else {
body.removeChildren();
}
// Remove any non-named parameters, which may reference locals.
int paramIndex = 0;
for (Node param = NodeUtil.getFunctionParameters(fnValue).getFirstChild(); param != null; ) {
final Node next = param.getNext();
makeParamSafe(param, paramIndex++);
param = next;
}
}
exprResult.getFirstChild().setJSDocInfo(info.build());
block.addChildToBack(exprResult);
nameToExprResult.put(fnName, exprResult);
}
// Copy other members.
for (MemberDefinition behaviorProp : behavior.nonPropertyMembersToCopy) {
String propName = behaviorProp.name.getString();
if (nameToExprResult.containsKey(propName)) {
nameToExprResult.get(propName).detach();
}
Node exprResult = IR.exprResult(NodeUtil.newQName(compiler, qualifiedPath + propName));
exprResult.srcrefTree(behaviorProp.name);
JSDocInfo.Builder info = getJSDocInfoBuilderForBehavior(behavior, behaviorProp);
if (behaviorProp.name.isGetterDef()) {
info = JSDocInfo.builder().parseDocumentation();
if (behaviorProp.info != null && behaviorProp.info.getReturnType() != null) {
info.recordType(behaviorProp.info.getReturnType());
}
}
exprResult.getFirstChild().setJSDocInfo(info.build());
block.addChildToBack(exprResult);
nameToExprResult.put(propName, exprResult);
}
}
}
Aggregations