FIX: LookupMethodCreator create too large method #8611
add Tests for LookupMothodCreator to JRE Suite
Fix wrong JavaObjectType for primitive char in AbstractSourceCreator
Add Test and Suite for AbstractSourceCreator
Change-Id: Ia28cc75cc8b936e4ea118ff26527236b693f165f
diff --git a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
index fed873c..71f7260 100644
--- a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
@@ -1,16 +1,14 @@
/*
* 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
+ * 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
+ * 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.i18n.rebind;
@@ -31,23 +29,48 @@
* Method creator to call the correct Map for the given Dictionary.
*/
class LookupMethodCreator extends AbstractMethodCreator {
+
+ /**
+ * Used partition size if no one is specified.
+ *
+ * Used in constructor without a partition size.
+ */
+ private static final int DEFAULT_PARTITIONS_SIZE = 500;
+
private JType returnType;
+ private final int partitionsSize;
+
+ /**
+ * Constructor for <code>LookupMethodCreator</code>. The default partition size of 500 is used.
+ *
+ * @param classCreator parent class creator
+ * @param returnType associated return type
+ *
+ * @see LookupMethodCreator#DEFAULT_PARTITIONS_SIZE
+ */
+ public LookupMethodCreator(AbstractGeneratorClassCreator classCreator, JType returnType) {
+ this(classCreator, returnType, DEFAULT_PARTITIONS_SIZE);
+ this.returnType = returnType;
+ }
+
/**
* Constructor for <code>LookupMethodCreator</code>.
*
* @param classCreator parent class creator
* @param returnType associated return type
+ * @param partitionsSize max numbers of lookups per method.
*/
- public LookupMethodCreator(AbstractGeneratorClassCreator classCreator,
- JType returnType) {
+ public LookupMethodCreator(AbstractGeneratorClassCreator classCreator, JType returnType,
+ int partitionsSize) {
super(classCreator);
this.returnType = returnType;
+ this.partitionsSize = partitionsSize;
}
@Override
- public void createMethodFor(TreeLogger logger, JMethod targetMethod,
- String key, ResourceList resourceList, GwtLocale locale) {
+ public void createMethodFor(TreeLogger logger, JMethod targetMethod, String key,
+ ResourceList resourceList, GwtLocale locale) {
createMethodFor(targetMethod);
}
@@ -65,6 +88,15 @@
return type;
}
+ void printLookup(String methodName) {
+ String body = "if(arg0.equals(" + wrap(methodName) + ")) {";
+ println(body);
+ indent();
+ printFound(methodName);
+ outdent();
+ println("}");
+ }
+
void createMethodFor(JMethod targetMethod) {
String template = "{0} target = ({0}) cache.get(arg0);";
String returnTypeName = getReturnTypeName();
@@ -76,24 +108,52 @@
outdent();
println("}");
JMethod[] methods = ((ConstantsWithLookupImplCreator) currentCreator).allInterfaceMethods;
- JType erasedType = returnType.getErasedType();
- for (int i = 0; i < methods.length; i++) {
- if (methods[i].getReturnType().getErasedType().equals(erasedType)
- && methods[i] != targetMethod) {
- String methodName = methods[i].getName();
- String body = "if(arg0.equals(" + wrap(methodName) + ")) {";
- println(body);
- indent();
- printFound(methodName);
- outdent();
- println("}");
- }
+
+ final int partitions = (methods.length / partitionsSize) + 1;
+ println(returnTypeName + " tmp;");
+ for (int i = 0; i < partitions; i++) {
+ println("tmp = " + targetMethod.getName() + i + "(arg0);");
+ println("if (tmp != null) {");
+ indent();
+ println("return tmp;");
+ outdent();
+ println("}");
}
+
String format = "throw new java.util.MissingResourceException(\"Cannot find constant ''\" +"
+ "{0} + \"''; expecting a method name\", \"{1}\", {0});";
- String result = MessageFormat.format(format, "arg0",
- this.currentCreator.getTarget().getQualifiedSourceName());
+ String result = MessageFormat.format(format, "arg0", this.currentCreator.getTarget()
+ .getQualifiedSourceName());
println(result);
+ outdent();
+ println("}");
+
+ println("");
+
+ final String argument0Type = targetMethod.getParameterTypes()[0].getQualifiedSourceName();
+ for (int p = 0; p < partitions; p++) {
+ final String templateNewMethod = "private {0} {1}{2}({3} arg0) '{";
+ final String header = MessageFormat.format(templateNewMethod, new Object[] {
+ returnTypeName, targetMethod.getName(), p, argument0Type});
+ println(header);
+ indent();
+ final JType erasedType = returnType.getErasedType();
+ for (int i = 0 + p * partitionsSize; i < methods.length && i < (p + 1)
+ * partitionsSize; i++) {
+ final JMethod method = methods[i];
+ if (method.getReturnType().getErasedType().equals(erasedType) && method != targetMethod) {
+ String methodName = method.getName();
+ printLookup(methodName);
+ }
+ }
+
+ println("return null;");
+ if (p < partitions - 1) {
+ outdent();
+ println("}");
+ println("");
+ }
+ }
}
void printFound(String methodName) {
diff --git a/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java b/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
index 5f5e31f..ebe7fa3 100644
--- a/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
+++ b/user/src/com/google/gwt/user/rebind/AbstractSourceCreator.java
@@ -82,6 +82,8 @@
protected static String getJavaObjectTypeFor(JPrimitiveType type) {
if (type == JPrimitiveType.INT) {
return "Integer";
+ } else if (type == JPrimitiveType.CHAR){
+ return "Character";
} else {
String s = type.getSimpleSourceName();
return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1);
diff --git a/user/test/com/google/gwt/i18n/I18NJreSuite.java b/user/test/com/google/gwt/i18n/I18NJreSuite.java
index cf5d875..e5f5565 100644
--- a/user/test/com/google/gwt/i18n/I18NJreSuite.java
+++ b/user/test/com/google/gwt/i18n/I18NJreSuite.java
@@ -17,6 +17,7 @@
import com.google.gwt.i18n.rebind.LocaleUtilsTest;
import com.google.gwt.i18n.rebind.LocalizableGeneratorTest;
+import com.google.gwt.i18n.rebind.LookupMethodCreatorTest;
import com.google.gwt.i18n.server.GwtLocaleTest;
import com.google.gwt.i18n.server.MessageFormatParserTest;
import com.google.gwt.i18n.server.PropertyCatalogFactoryTest;
@@ -28,6 +29,7 @@
import com.google.gwt.i18n.shared.FirstStrongDirectionEstimatorTest;
import com.google.gwt.i18n.shared.WordCountDirectionEstimatorTest;
+import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -59,6 +61,7 @@
*/
// suite.addTestSuite(TypeOracleMessageTest.class);
suite.addTestSuite(WordCountDirectionEstimatorTest.class);
+ suite.addTest(new JUnit4TestAdapter(LookupMethodCreatorTest.class));
// $JUnit-END$
return suite;
diff --git a/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java b/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java
new file mode 100644
index 0000000..c9708eb
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/rebind/LookupMethodCreatorTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2017 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.i18n.rebind;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracleException;
+import com.google.gwt.dev.javac.TypeOracleTestingUtils;
+import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
+import com.google.gwt.dev.shell.FailErrorLogger;
+import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link LookupMethodCreator}.
+ */
+public class LookupMethodCreatorTest {
+
+ private TreeLogger logger = new FailErrorLogger();
+
+ private LookupMethodCreator underTest;
+
+ private SourceWriter sw = new StringSourceWriter();
+
+ private TypeOracle oracle;
+
+ private JMethod method;
+
+ private static final MockJavaResource SINGLE_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.SingleEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface SingleEntryMessage extends foo.Lookup {\n");
+ code.append(" String singleEntry();\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource FOUR_ENTRY_MESSAGES = new MockJavaResource(
+ "foo.FourEntryMessage") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface FourEntryMessage extends foo.Lookup {\n");
+ code.append(" String first();\n");
+ code.append(" String second();\n");
+ code.append(" String third();\n");
+ code.append(" String fourth();\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private static final MockJavaResource LOOKUP = new MockJavaResource("foo.Lookup") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package foo;\n");
+ code.append("public interface Lookup {\n");
+ code.append(" String getString(String arg0);\n");
+ code.append("}");
+ return code;
+ }
+ };
+
+ private void initLookupMethodCreator(MockJavaResource resource, int partitionsSize) {
+ JClassType clazz = oracle.findType(resource.getTypeName());
+ ConstantsWithLookupImplCreator mockCreator;
+ try {
+ mockCreator = new ConstantsWithLookupImplCreator(logger, sw, clazz, mock(ResourceList.class),
+ oracle);
+
+ JType stringType = oracle.findType("java.lang.String");
+ method = oracle.findType(LOOKUP.getTypeName()).findMethod("getString", new JType[] {
+ stringType});
+ underTest = new LookupMethodCreator(mockCreator, stringType, partitionsSize);
+ } catch (UnableToCompleteException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Before
+ public void setupUp() throws TypeOracleException, UnableToCompleteException {
+ oracle = TypeOracleTestingUtils.buildStandardTypeOracleWith(logger, SINGLE_ENTRY_MESSAGES,
+ FOUR_ENTRY_MESSAGES, LOOKUP);
+ initLookupMethodCreator(SINGLE_ENTRY_MESSAGES, 3);
+ }
+
+ /**
+ * Test the "golden output".<br>
+ * In general, those tests are more trouble than they are worth but this will be a simple test for
+ * verify the general structure.
+ *
+ * old version looks: <pre>
+ * java.lang.String target = (java.lang.String) cache.get(arg0);
+ * if (target != null) {
+ * return target;
+ * }
+ * if(arg0.equals("singleEntry")) {
+ * return singleEntry();
+ * }
+ * throw new java.util.MissingResourceException("Cannot find constant '" +arg0 + "'; expecting a method name", "foo.TestMessage", arg0);
+ * </pre>
+ *
+ * with first partition lookup it generates:
+ *
+ * <pre>
+ * java.lang.String target = (java.lang.String) cache.get(arg0);
+ * if (target != null) {
+ * return target;
+ * }
+ * java.lang.String tmp;
+ * tmp = getString0(arg0);
+ * if (tmp != null) {
+ * return tmp;
+ * }
+ * throw new java.util.MissingResourceException("Cannot find constant '" +arg0 + "'; expecting a method name", "foo.TestMessage", arg0);
+ * }
+ *
+ * public java.lang.String getString0(java.lang.String arg0) {
+ * if(arg0.equals("singleEntry")) {
+ * return singleEntry();
+ * }
+ * return null;
+ *
+ * </pre>
+ */
+ @Test
+ public void testCreateMethodForJMethodForSingleEntry() {
+ underTest.createMethodFor(method);
+
+ SourceWriter expected = new StringSourceWriter();
+ expected.println("java.lang.String target = (java.lang.String) cache.get(arg0);");
+ expected.println("if (target != null) {");
+ expected.indent();
+ expected.println("return target;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("java.lang.String tmp;");
+ expected.println("tmp = getString0(arg0);");
+ expected.println("if (tmp != null) {");
+ expected.indent();
+ expected.println("return tmp;");
+ expected.outdent();
+ expected.println("}");
+
+ expected.println("throw new java.util.MissingResourceException("
+ + "\"Cannot find constant '\" +arg0 + \"'; expecting a method name\", \"foo.SingleEntryMessage\", arg0);");
+
+ // check partition method
+ assertTrue("No partition lookup created.", sw.toString().contains(
+ "java.lang.String getString0(java.lang.String arg0) {"));
+
+ expected.println("}");
+ expected.println();
+ expected.println("private java.lang.String getString0(java.lang.String arg0) {");
+ expected.indent();
+ expected.println("if(arg0.equals(\"singleEntry\")) {");
+ expected.indent();
+ expected.println("return singleEntry();");
+ expected.outdent();
+ expected.println("}");
+ expected.println("return null;");
+ expected.outdent();
+
+ assertEquals("Wrong source Lookup created.", expected.toString(), sw.toString());
+ }
+
+ @Test
+ public void testCreateMethodForJMethodForMultiMessageEntryCreateTwoPartitions() {
+ initLookupMethodCreator(FOUR_ENTRY_MESSAGES, 3);
+ underTest.createMethodFor(method);
+
+ String actual = sw.toString();
+
+ assertTrue("Missing partition lookup method (getString0).", actual.contains(
+ "java.lang.String getString0(java.lang.String arg0) {"));
+
+ assertTrue("Missing partition lookup method (getString1).", actual.contains(
+ "java.lang.String getString1(java.lang.String arg0) {"));
+ }
+
+ @Test
+ public void testPrintFound() {
+ underTest.printFound("callTest");
+
+ String returnStatement = sw.toString();
+ assertEquals("return callTest();\n", returnStatement);
+ }
+
+ @Test
+ public void testGetReturnTypeName() {
+ String returnType = underTest.getReturnTypeName();
+ assertEquals("java.lang.String", returnType);
+ }
+
+ @Test
+ public void testGetReturnTypeNameForPrimitveTypes() {
+ for (JPrimitiveType primitiveType : JPrimitiveType.values()) {
+ LookupMethodCreator primitiveMethodCreator = new LookupMethodCreator(null, primitiveType, 2);
+ String returnType = primitiveMethodCreator.getReturnTypeName();
+ String expectedType = primitiveType.getQualifiedBoxedSourceName().substring("java.lang."
+ .length());
+ assertEquals("Wrong Return Type for primitve type", expectedType, returnType);
+ }
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/UserJreSuite.java b/user/test/com/google/gwt/user/UserJreSuite.java
new file mode 100644
index 0000000..d6b3369
--- /dev/null
+++ b/user/test/com/google/gwt/user/UserJreSuite.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 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.user;
+
+import com.google.gwt.user.rebind.AbstractSourceCreatorTest;
+
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * User tests running as a regular JRE test.
+ */
+public class UserJreSuite {
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Non-browser tests for com.google.gwt.user");
+
+ // $JUnit-BEGIN$
+ suite.addTest(new JUnit4TestAdapter(AbstractSourceCreatorTest.class));
+ // $JUnit-END$
+
+ return suite;
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java b/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java
new file mode 100644
index 0000000..8a5b3c7
--- /dev/null
+++ b/user/test/com/google/gwt/user/rebind/AbstractSourceCreatorTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 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.user.rebind;
+
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class AbstractSourceCreatorTest {
+
+ @Test
+ public void testGetJavaObjectTypeForPrimitveTypes() {
+ for (JPrimitiveType primitiveType : JPrimitiveType.values()) {
+ String returnType = AbstractSourceCreator.getJavaObjectTypeFor(primitiveType);
+ String expectedType = primitiveType.getQualifiedBoxedSourceName().substring("java.lang."
+ .length());
+ assertEquals("Wrong Return Type for primitve type", expectedType, returnType);
+ }
+ }
+
+}