blob: 515e9d5df4661b1f62adb528e6238f84fdc98594 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.dev.PropertyAndBindingInfo;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.PermutationProperties;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.AccessModifier;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JBlock;
import com.google.gwt.dev.jjs.ast.JCaseStatement;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JPermutationDependentValue;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JSwitchStatement;
import com.google.gwt.dev.jjs.ast.RuntimeConstants;
import com.google.gwt.thirdparty.guava.common.base.Function;
import com.google.gwt.thirdparty.guava.common.base.Joiner;
import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Replaces any "GWT.create()" calls with a new expression for the actual result of the deferred
* binding decision and System.getProperty() with the actual value for that property.
* <p>
* When properties are collapsed (e.g. in soft permutations) a method is create that resolves
* the value at runtime.
* </p>
*
*/
public class ResolvePermutationDependentValues {
private class ValueReplacer extends JModVisitor {
@Override
public void endVisit(JPermutationDependentValue x, Context ctx) {
if (x.isTypeRebind()) {
ctx.replaceMe(rebindClassExpression(x));
return;
}
assert x.isProperty();
ctx.replaceMe(propertyValueExpression(x));
}
private JExpression propertyValueExpression(JPermutationDependentValue x) {
List<String> propertyValues = props.getConfigurationProperties().getStrings(x.getRequestedValue());
String propertyValue = propertyValues.isEmpty() ? null : Joiner.on(",").join(propertyValues);
if (propertyValue != null) {
// It is a configuration property.
return program.getLiteral(x.getSourceInfo(), propertyValue);
}
if (isSoftPermutationProperty(x.getRequestedValue())) {
JMethod method = getOrCreateSoftPropertyMethod(x.getSourceInfo(), x.getRequestedValue());
return new JMethodCall(x.getSourceInfo(), null, method);
}
propertyValue = commonPropertyAndBindingInfo.getPropertyValue(x.getRequestedValue());
if (propertyValue != null) {
return program.getLiteral(x.getSourceInfo(), propertyValue);
}
return x.getDefaultValueExpression();
}
private JExpression rebindClassExpression(JPermutationDependentValue x) {
if (isSoftTypeRebind(x.getRequestedValue())) {
JMethod method = getOrCreateTypeRebindMethod(x.getSourceInfo(), x.getRequestedValue(),
x.getResultValues(), x.getResultExpressions());
return new JMethodCall(x.getSourceInfo(), null, method);
}
return computeInstantiationExpression(x);
}
}
public static boolean exec(JProgram program, PermutationProperties props,
List<PropertyAndBindingInfo> propertyAndBindingInfo) {
return new ResolvePermutationDependentValues(program, props, propertyAndBindingInfo).execImpl();
}
private final JProgram program;
private final PermutationProperties props;
private final Set<String> bindingPropertyNames;
private final List<PropertyAndBindingInfo> permutationPropertyAndBindingInfo;
private final PropertyAndBindingInfo commonPropertyAndBindingInfo;
private final JClassType holderType;
private final JMethod permutationIdMethod;
private final Map<String, JMethod> softPermutationMethods = Maps.newHashMap();
private ResolvePermutationDependentValues(JProgram program, PermutationProperties props,
List<PropertyAndBindingInfo> gwtCreateAnswers) {
this.program = program;
this.permutationPropertyAndBindingInfo = gwtCreateAnswers;
this.props = props;
this.bindingPropertyNames = FluentIterable.from(props.getBindingProperties()).transform(
new Function<BindingProperty, String>() {
@Override
public String apply(BindingProperty bindingProperty) {
return bindingProperty.getName();
}
}).toSet();
this.commonPropertyAndBindingInfo = PropertyAndBindingInfo.getCommonAnswers(gwtCreateAnswers);
this.holderType = (JClassType) program.getIndexedType("CollapsedPropertyHolder");
this.permutationIdMethod = program.getIndexedMethod(
RuntimeConstants.COLLAPSED_PROPERTY_HOLDER_GET_PERMUTATION_ID);
}
public JExpression computeInstantiationExpression(JPermutationDependentValue x) {
String reqType = x.getRequestedValue();
// Rebinds are always on a source type name.
String reboundClassName = commonPropertyAndBindingInfo.getReboundType(reqType);
if (reboundClassName == null) {
// The fact that we already compute every rebind permutation before
// compiling should prevent this case from ever happening in real life.
//
throw new InternalCompilerException("Unexpected failure to rebind '" + reqType + "'");
}
assert program.getFromTypeMap(reboundClassName) != null;
int index = x.getResultValues().indexOf(reboundClassName);
if (index == -1) {
throw new InternalCompilerException("No matching rebind result in all rebind results!");
}
return x.getResultExpressions().get(index);
}
private boolean execImpl() {
ValueReplacer valueReplacer = new ValueReplacer();
valueReplacer.accept(program);
return valueReplacer.didChange();
}
private boolean isSoftTypeRebind(String requestType) {
return !commonPropertyAndBindingInfo.containsType(requestType);
}
private boolean isSoftPermutationProperty(String propertyName) {
return bindingPropertyNames.contains(propertyName) &&
!commonPropertyAndBindingInfo.containsProperty(propertyName);
}
private JMethod getOrCreateSoftPropertyMethod(final SourceInfo info, String propertyName) {
JMethod toReturn = softPermutationMethods.get(propertyName);
if (toReturn != null) {
return toReturn;
}
Multimap<String, Integer> resultsToPermutations = PropertyAndBindingInfo
.getPermutationIdsByPropertyName(permutationPropertyAndBindingInfo, propertyName);
List<String> propertyValues = Lists.newArrayList(resultsToPermutations.keySet());
return createReboundValueSelectorMethod(info, "property_", propertyName,
program.getTypeJavaLangString(), propertyValues,
FluentIterable.from(propertyValues).transform(
new Function<String, JExpression>() {
@Override
public JExpression apply(String s) {
return program.getLiteral(info, s);
}
}).toList(), resultsToPermutations);
}
private JMethod getOrCreateTypeRebindMethod(SourceInfo info, String requestType,
List<String> resultTypes, List<JExpression> instantiationExpressions) {
assert resultTypes.size() == instantiationExpressions.size();
JMethod toReturn = softPermutationMethods.get(requestType);
if (toReturn != null) {
return toReturn;
}
Multimap<String, Integer> resultsToPermutations = PropertyAndBindingInfo
.getPermutationIdsByRequestTypes(permutationPropertyAndBindingInfo, requestType);
return createReboundValueSelectorMethod(info, "create_", requestType,
program.getTypeJavaLangObject().strengthenToNonNull(), resultTypes,
instantiationExpressions, resultsToPermutations);
}
private JMethod createReboundValueSelectorMethod(SourceInfo info, String prefix,
String parameterType, JReferenceType returntype, List<String> results,
List<JExpression> resultExpressions, Multimap<String, Integer> resultsToPermutations) {
// Pick the most-used result type to emit less code
String mostUsed = mostUsedValue(resultsToPermutations);
assert mostUsed != null;
JMethod toReturn;
info = info.makeChild(SourceOrigin.UNKNOWN);
// c_g_g_d_c_i_DOMImpl
toReturn =
new JMethod(info, prefix + parameterType.replace("_", "_1").replace('.', '_'), holderType,
returntype, false, true, true, AccessModifier.PUBLIC);
toReturn.setBody(new JMethodBody(info));
holderType.addMethod(toReturn);
toReturn.freezeParamTypes();
info.addCorrelation(info.getCorrelator().by(toReturn));
softPermutationMethods.put(parameterType, toReturn);
// Used in the return statement at the end
JExpression mostUsedExpression = null;
JBlock switchBody = new JBlock(info);
for (int i = 0, j = results.size(); i < j; i++) {
String resultType = results.get(i);
JExpression expression = resultExpressions.get(i);
Collection<Integer> permutations = resultsToPermutations.get(resultType);
if (permutations.isEmpty()) {
// This rebind result is unused in this permutation
continue;
} else if (resultType.equals(mostUsed)) {
// Save off the fallback expression and go onto the next type
mostUsedExpression = expression;
continue;
}
for (int permutationId : permutations) {
// case 33:
switchBody.addStmt(new JCaseStatement(info, program.getLiteralInt(permutationId)));
}
// return new FooImpl();
switchBody.addStmt(expression.makeReturnStatement());
}
assert switchBody.getStatements().size() > 0 : "No case statement emitted "
+ "for supposedly soft-rebind " + parameterType;
// switch (CollapsedPropertyHolder.getPermutationId()) { ... }
JSwitchStatement sw =
new JSwitchStatement(info, new JMethodCall(info, null, permutationIdMethod), switchBody);
// return new FallbackImpl(); at the very end.
assert mostUsedExpression != null : "No most-used expression";
JReturnStatement fallbackReturn = mostUsedExpression.makeReturnStatement();
JMethodBody body = (JMethodBody) toReturn.getBody();
body.getBlock().addStmt(sw);
body.getBlock().addStmt(fallbackReturn);
return toReturn;
}
private String mostUsedValue(Multimap<String, Integer> resultsToPermutations) {
String mostUsed = null;
int max = 0;
for (String key : resultsToPermutations.keySet()) {
int size = resultsToPermutations.get(key).size();
if (size > max) {
max = size;
mostUsed = key;
}
}
return mostUsed;
}
}