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); + } + } + +}