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, 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.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassRewriter method rewritePolymerCall.
/**
* Rewrites a given call to Polymer({}) to a set of declarations and assignments which can be
* understood by the compiler.
*
* @param exprRoot The root expression of the call to Polymer({}).
* @param cls The extracted {@link PolymerClassDefinition} for the Polymer element created by this
* call.
*/
void rewritePolymerCall(Node exprRoot, final PolymerClassDefinition cls, boolean isInGlobalScope) {
Node objLit = checkNotNull(cls.descriptor);
// Add {@code @lends} to the object literal.
JSDocInfoBuilder objLitDoc = new JSDocInfoBuilder(true);
objLitDoc.recordLends(cls.target.getQualifiedName() + ".prototype");
objLit.setJSDocInfo(objLitDoc.build());
addTypesToFunctions(objLit, cls.target.getQualifiedName(), cls.defType);
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(objLit, compiler);
PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(objLit, compiler);
for (MemberDefinition prop : cls.props) {
if (prop.value.isObjectLit()) {
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(prop.value, compiler);
}
}
// For simplicity add everything into a block, before adding it to the AST.
Node block = IR.block();
JSDocInfoBuilder constructorDoc = this.getConstructorDoc(cls);
// Remove the original constructor JS docs from the objlit.
Node ctorKey = cls.constructor.value.getParent();
if (ctorKey != null) {
ctorKey.removeProp(Node.JSDOC_INFO_PROP);
}
if (cls.target.isGetProp()) {
// foo.bar = Polymer({...});
Node assign = IR.assign(cls.target.cloneTree(), cls.constructor.value.cloneTree());
NodeUtil.markNewScopesChanged(assign, compiler);
assign.setJSDocInfo(constructorDoc.build());
Node exprResult = IR.exprResult(assign);
exprResult.useSourceInfoIfMissingFromForTree(cls.target);
block.addChildToBack(exprResult);
} else {
// var foo = Polymer({...}); OR Polymer({...});
Node var = IR.var(cls.target.cloneTree(), cls.constructor.value.cloneTree());
NodeUtil.markNewScopesChanged(var, compiler);
var.useSourceInfoIfMissingFromForTree(exprRoot);
var.setJSDocInfo(constructorDoc.build());
block.addChildToBack(var);
}
appendPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype.");
appendBehaviorMembersToBlock(cls, block);
ImmutableList<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block);
ImmutableList<MemberDefinition> attributeReflectedProps = parseAttributeReflectedProperties(cls);
addInterfaceExterns(cls, readOnlyProps, attributeReflectedProps);
removePropertyDocs(objLit, PolymerClassDefinition.DefinitionType.ObjectLiteral);
Node statements = block.removeChildren();
Node parent = exprRoot.getParent();
// global.
if (this.polymerVersion == 1 && !isInGlobalScope && !cls.target.isGetProp()) {
Node scriptNode = NodeUtil.getEnclosingScript(parent);
scriptNode.addChildrenToFront(statements);
compiler.reportChangeToChangeScope(scriptNode);
} else {
Node beforeRoot = exprRoot.getPrevious();
if (beforeRoot == null) {
parent.addChildrenToFront(statements);
} else {
parent.addChildrenAfter(statements, beforeRoot);
}
compiler.reportChangeToEnclosingScope(parent);
}
compiler.reportChangeToEnclosingScope(statements);
// we might need to update the FeatureSet.
if (cls.features != null) {
Node scriptNode = NodeUtil.getEnclosingScript(parent);
FeatureSet oldFeatures = (FeatureSet) scriptNode.getProp(Node.FEATURE_SET);
FeatureSet newFeatures = oldFeatures.union(cls.features);
if (!newFeatures.equals(oldFeatures)) {
scriptNode.putProp(Node.FEATURE_SET, newFeatures);
compiler.reportChangeToChangeScope(scriptNode);
}
}
if (NodeUtil.isNameDeclaration(exprRoot)) {
Node assignExpr = varToAssign(exprRoot);
parent.replaceChild(exprRoot, assignExpr);
compiler.reportChangeToEnclosingScope(assignExpr);
}
// with the class members.
if (polymerVersion > 1 && propertyRenamingEnabled && cls.descriptor != null) {
Node props = NodeUtil.getFirstPropMatchingKey(cls.descriptor, "properties");
if (props != null && props.isObjectLit()) {
addObjectReflectionCall(cls, props);
}
}
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassRewriter method rewritePolymerClassDeclaration.
/**
* Rewrites a class which extends Polymer.Element to a set of declarations and assignments which
* can be understood by the compiler.
*
* @param clazz The class node
* @param cls The extracted {@link PolymerClassDefinition} for the Polymer element created by this
* call.
*/
void rewritePolymerClassDeclaration(Node clazz, final PolymerClassDefinition cls, boolean isInGlobalScope) {
if (cls.descriptor != null) {
addTypesToFunctions(cls.descriptor, cls.target.getQualifiedName(), cls.defType);
}
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(NodeUtil.getClassMembers(clazz), compiler);
for (MemberDefinition prop : cls.props) {
if (prop.value.isObjectLit()) {
PolymerPassStaticUtils.switchDollarSignPropsToBrackets(prop.value, compiler);
}
}
// For simplicity add everything into a block, before adding it to the AST.
Node block = IR.block();
appendPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype.");
ImmutableList<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block);
ImmutableList<MemberDefinition> attributeReflectedProps = parseAttributeReflectedProperties(cls);
addInterfaceExterns(cls, readOnlyProps, attributeReflectedProps);
// If an external interface is required, mark the class as implementing it
if (!readOnlyProps.isEmpty() || !attributeReflectedProps.isEmpty()) {
JSDocInfoBuilder classInfo = JSDocInfoBuilder.maybeCopyFrom(clazz.getJSDocInfo());
String interfaceName = getInterfaceName(cls);
JSTypeExpression interfaceType = new JSTypeExpression(new Node(Token.BANG, IR.string(interfaceName)), VIRTUAL_FILE);
classInfo.recordImplementedInterface(interfaceType);
clazz.setJSDocInfo(classInfo.build());
}
if (block.hasChildren()) {
removePropertyDocs(cls.descriptor, cls.defType);
Node stmt = NodeUtil.getEnclosingStatement(clazz);
stmt.getParent().addChildrenAfter(block.removeChildren(), stmt);
compiler.reportChangeToEnclosingScope(stmt);
}
addReturnTypeIfMissing(cls, "is", new JSTypeExpression(IR.string("string"), VIRTUAL_FILE));
Node type = new Node(Token.BANG);
Node array = IR.string("Array");
type.addChildToBack(array);
Node arrayTemplateType = new Node(Token.BLOCK, IR.string("string"));
array.addChildToBack(arrayTemplateType);
addReturnTypeIfMissing(cls, "observers", new JSTypeExpression(type, VIRTUAL_FILE));
addReturnTypeIfMissing(cls, "properties", new JSTypeExpression(IR.string(POLYMER_ELEMENT_PROP_CONFIG), VIRTUAL_FILE));
// with the class members.
if (propertyRenamingEnabled && cls.descriptor != null) {
addObjectReflectionCall(cls, cls.descriptor);
}
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.
the class PolymerClassRewriter method addInterfaceExterns.
/**
* Adds an interface for the given ClassDefinition to externs. This allows generated setter
* functions for read-only properties to avoid renaming altogether.
*
* @see https://www.polymer-project.org/0.8/docs/devguide/properties.html#read-only
*/
private void addInterfaceExterns(final PolymerClassDefinition cls, List<MemberDefinition> readOnlyProps, List<MemberDefinition> attributeReflectedProps) {
Node block = IR.block();
String interfaceName = getInterfaceName(cls);
Node fnNode = NodeUtil.emptyFunction();
compiler.reportChangeToChangeScope(fnNode);
Node varNode = IR.var(NodeUtil.newQName(compiler, interfaceName), fnNode);
JSDocInfoBuilder info = new JSDocInfoBuilder(true);
info.recordInterface();
varNode.setJSDocInfo(info.build());
block.addChildToBack(varNode);
if (polymerVersion == 1) {
// For Polymer 1, all declared properties are non-renameable
appendPropertiesToBlock(cls, block, interfaceName + ".prototype.");
} else {
List<MemberDefinition> interfaceProperties = new ArrayList<>();
interfaceProperties.addAll(readOnlyProps);
if (attributeReflectedProps != null) {
interfaceProperties.addAll(attributeReflectedProps);
}
// For Polymer 2, only read-only properties and reflectToAttribute properties are
// non-renameable. Other properties follow the ALL_UNQUOTED renaming rules.
PolymerClassDefinition tmpDef = new PolymerClassDefinition(cls.defType, cls.definition, cls.target, cls.descriptor, null, null, null, interfaceProperties, null, null);
// disallow renaming of readonly properties
appendPropertiesToBlock(tmpDef, block, interfaceName + ".prototype.");
}
for (MemberDefinition prop : readOnlyProps) {
// Add all _set* functions to avoid renaming.
String propName = prop.name.getString();
String setterName = "_set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
Node setterExprNode = IR.exprResult(NodeUtil.newQName(compiler, interfaceName + ".prototype." + setterName));
JSDocInfoBuilder setterInfo = new JSDocInfoBuilder(true);
JSTypeExpression propType = PolymerPassStaticUtils.getTypeFromProperty(prop, compiler);
setterInfo.recordParameter(propName, propType);
setterExprNode.getFirstChild().setJSDocInfo(setterInfo.build());
block.addChildToBack(setterExprNode);
}
block.useSourceInfoIfMissingFromForTree(polymerElementExterns);
Node scopeRoot = polymerElementExterns;
if (!scopeRoot.isScript()) {
scopeRoot = scopeRoot.getParent();
}
Node stmts = block.removeChildren();
scopeRoot.addChildrenToBack(stmts);
compiler.reportChangeToEnclosingScope(stmts);
}
use of com.google.javascript.jscomp.PolymerPass.MemberDefinition 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