Skip to content

Commit

Permalink
PolymerPass: 2 Behaviors fixes -
Browse files Browse the repository at this point in the history
1) Allow inline object literals in the behaviors array.
2) If two or more behaviors for the same elements define the same function, only copy the last behavior's function definition over to the element prototype.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=92786023
  • Loading branch information
jklein24 authored and dimvar committed May 5, 2015
1 parent d536a7d commit c6d9df6
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/com/google/javascript/jscomp/PolymerPass.java
Expand Up @@ -30,6 +30,7 @@
import com.google.javascript.rhino.Token;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -298,6 +299,12 @@ private List<BehaviorDefinition> extractBehaviors(Node behaviorArray) {

ImmutableList.Builder<BehaviorDefinition> behaviors = ImmutableList.builder();
for (Node behaviorName : behaviorArray.children()) {
if (behaviorName.isObjectLit()) {
behaviors.add(new BehaviorDefinition(
extractProperties(behaviorName), getBehaviorFunctionsToCopy(behaviorName)));
continue;
}

Name behaviorGlobalName = globalNames.getSlot(behaviorName.getQualifiedName());
if (behaviorGlobalName == null) {
compiler.report(JSError.make(behaviorName, POLYMER_UNQUALIFIED_BEHAVIOR));
Expand Down Expand Up @@ -488,15 +495,23 @@ private void appendPropertiesToBlock(final ClassDefinition cls, Node block) {
*/
private void appendBehaviorFunctionsToBlock(final ClassDefinition 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();
// Avoid copying over the same function twice. The last definition always wins.
if (nameToExprResult.containsKey(fnName)) {
block.removeChild(nameToExprResult.get(fnName));
}

Node exprResult = IR.exprResult(IR.assign(
NodeUtil.newQName(compiler, qualifiedPath + behaviorFunction.name.getString()),
NodeUtil.newQName(compiler, qualifiedPath + fnName),
behaviorFunction.value.cloneTree()));
JSDocInfoBuilder info = JSDocInfoBuilder.maybeCopyFrom(behaviorFunction.info);

exprResult.getFirstChild().setJSDocInfo(info.build());
block.addChildToBack(exprResult);
nameToExprResult.put(fnName, exprResult);
}
}
}
Expand Down
142 changes: 142 additions & 0 deletions test/com/google/javascript/jscomp/PolymerPassTest.java
Expand Up @@ -675,6 +675,148 @@ public void testArrayBehavior() {
"});"));
}

public void testInlineLiteralBehavior() {
String behaviors = Joiner.on("\n").join(
"var FunBehavior = {",
" properties: {",
" isFun: Boolean",
" },",
" /** @param {string} funAmount */",
" doSomethingFun: function(funAmount) { alert('Something ' + funAmount + ' fun!'); },",
" /** @override */",
" created: function() {}",
"};",
"var SuperCoolBehaviors = [FunBehavior, {",
" properties: {",
" howRad: Number",
" },",
" /** @param {number} radAmount */",
" doSomethingRad: function(radAmount) { alert('Something ' + radAmount + ' rad!'); },",
" /** @override */",
" ready: function() {}",
"}];");

test(behaviors + Joiner.on("\n").join(
"var A = Polymer({",
" is: 'x-element',",
" properties: {",
" pets: {",
" type: Array,",
" notify: true,",
" },",
" name: String,",
" },",
" behaviors: [ SuperCoolBehaviors ],",
"});"),

behaviors + Joiner.on("\n").join(
"/** @constructor @extends {PolymerElement} @export @implements {PolymerAInterface}*/",
"var A = function() {};",
"/** @type {!Array} @export */",
"A.prototype.pets;",
"/** @type {string} @export */",
"A.prototype.name;",
"/** @type {boolean} @export */",
"A.prototype.isFun;",
"/** @type {number} @export */",
"A.prototype.howRad;",
"/** @param {string} funAmount */",
"A.prototype.doSomethingFun = function(funAmount) {",
" alert('Something ' + funAmount + ' fun!');",
"};",
"/** @param {number} radAmount */",
"A.prototype.doSomethingRad = function(radAmount) {",
" alert('Something ' + radAmount + ' rad!');",
"};",
"A = Polymer(/** @lends {A.prototype} */ {",
" is: 'x-element',",
" properties: {",
" pets: {",
" type: Array,",
" notify: true,",
" },",
" name: String,",
" },",
" behaviors: [ SuperCoolBehaviors ],",
"});"));
}

/**
* If an element has two or more behaviors which define the same function, only the last
* behavior's function should be copied over to the element's prototype.
*/
public void testBehaviorFunctionOverriding() {
String behaviors = Joiner.on("\n").join(
"var FunBehavior = {",
" properties: {",
" isFun: Boolean",
" },",
" /** @param {boolean} boredYet */",
" doSomething: function(boredYet) { alert(boredYet + ' ' + this.isFun); },",
" /** @override */",
" created: function() {}",
"};",
"var RadBehavior = {",
" properties: {",
" howRad: Number",
" },",
" /** @param {boolean} boredYet */",
" doSomething: function(boredYet) { alert(boredYet + ' ' + this.howRad); },",
" /** @override */",
" ready: function() {}",
"};",
"var SuperCoolBehaviors = [FunBehavior, RadBehavior];",
"var BoringBehavior = {",
" properties: {",
" boringString: String",
" },",
" /** @param {boolean} boredYet */",
" doSomething: function(boredYet) { alert(boredYet + ' ' + this.boringString); },",
"};");

test(behaviors + Joiner.on("\n").join(
"var A = Polymer({",
" is: 'x-element',",
" properties: {",
" pets: {",
" type: Array,",
" notify: true,",
" },",
" name: String,",
" },",
" behaviors: [ SuperCoolBehaviors, BoringBehavior ],",
"});"),

behaviors + Joiner.on("\n").join(
"/** @constructor @extends {PolymerElement} @export @implements {PolymerAInterface}*/",
"var A = function() {};",
"/** @type {!Array} @export */",
"A.prototype.pets;",
"/** @type {string} @export */",
"A.prototype.name;",
"/** @type {boolean} @export */",
"A.prototype.isFun;",
"/** @type {number} @export */",
"A.prototype.howRad;",
"/** @type {string} @export */",
"A.prototype.boringString;",
"/** @param {boolean} boredYet */",
"A.prototype.doSomething = function(boredYet) {",
" alert(boredYet + ' ' + this.boringString);",
"};",
"A = Polymer(/** @lends {A.prototype} */ {",
" is: 'x-element',",
" properties: {",
" pets: {",
" type: Array,",
" notify: true,",
" },",
" name: String,",
" },",
" behaviors: [ SuperCoolBehaviors, BoringBehavior ],",
"});"));
}

public void testBehaviorReadOnlyProp() {
String js = Joiner.on("\n").join(
"var FunBehavior = {",
Expand Down

0 comments on commit c6d9df6

Please sign in to comment.