Revision: 8748
Author: jbrosenb...@google.com
Date: Fri Sep 10 06:27:47 2010
Log: Enum name obfuscation. Added configuration property
(compiler.enum.obfuscate.names) to optionally obfuscatate enum names from
production output.
Review at http://gwt-code-reviews.appspot.com/827802
http://code.google.com/p/google-web-toolkit/source/detail?r=8748
Added:
/trunk/dev/core/src/com/google/gwt/dev/jjs/EnumNameObfuscator.java
/trunk/user/test/com/google/gwt/dev/jjs/EnumsWithNameObfuscationSuite.gwt.xml
/trunk/user/test/com/google/gwt/dev/jjs/EnumsWithNameObfuscationSuite.java
/trunk/user/test/com/google/gwt/dev/jjs/test/EnumsWithNameObfuscationTest.java
/trunk/user/test/com/google/gwt/emultest/java/math/MathContextTest.java
Modified:
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
/trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml
/trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java
/trunk/user/src/com/google/gwt/requestfactory/shared/WriteOperation.java
/trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
/trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
/trunk/user/src/com/google/gwt/rpc/server/SimplePayloadDecoder.java
/trunk/user/super/com/google/gwt/emul/java/lang/Enum.java
/trunk/user/super/com/google/gwt/emul/java/math/MathContext.java
/trunk/user/super/com/google/gwt/emul/java/math/RoundingMode.java
/trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
/trunk/user/test/com/google/gwt/emultest/EmulSuite.java
/trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/EnumNameObfuscator.java Fri
Sep 10 06:27:47 2010
@@ -0,0 +1,171 @@
+/*
+ * 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.dev.jjs;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNonNullType;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JThisRef;
+
+import java.util.List;
+
+/**
+ * Performs optimizations on Enums.
+ */
+public class EnumNameObfuscator {
+
+ private static class EnumNameCallChecker extends JModVisitor {
+
+ private final TreeLogger logger;
+ private final JMethod enumNameMethod;
+ private final JMethod enumToStringMethod;
+ private final JMethod enumValueOfMethod;
+ private final JDeclaredType classType;
+ private final JDeclaredType enumType;
+ private final JDeclaredType stringType;
+
+ public EnumNameCallChecker(JProgram jprogram, TreeLogger logger) {
+ this.logger = logger;
+ this.enumNameMethod = jprogram.getIndexedMethod("Enum.name");
+ this.enumToStringMethod = jprogram.getIndexedMethod("Enum.toString");
+ this.classType = jprogram.getIndexedType("Class");
+ this.enumType = jprogram.getIndexedType("Enum");
+ this.stringType = jprogram.getIndexedType("String");
+
+ /*
+ * Find the correct version of enumValueOfMethod.
+ * Note: it doesn't work to check against a ref returned by
+ * jprogram.getIndexedMethod("Enum.valueOf"), since there are
+ * 2 different versions of Enum.valueOf in our jre emulation library,
+ * and the indexed ref won't reliably flag the public instance, which
+ * is the one we want here (i.e. Enum.valueOf(Class<T>,String)).
+ * The other version is protected, but is called by the generated
+ * constructors for sub-classes of Enum, and we don't want to warn
for
+ * those cases.
+ */
+ JMethod foundMethod = null;
+ List<JMethod> enumMethods = enumType.getMethods();
+ for (JMethod enumMethod : enumMethods) {
+ if ("valueOf".equals(enumMethod.getName())) {
+ List<JParameter> jParameters = enumMethod.getParams();
+ if (jParameters.size() == 2 &&
+ jParameters.get(0).getType() == classType &&
+ jParameters.get(1).getType() == stringType) {
+ foundMethod = enumMethod;
+ break;
+ }
+ }
+ }
+ this.enumValueOfMethod = foundMethod;
+ }
+
+ @Override
+ public void endVisit(JMethodCall x, Context ctx) {
+ JMethod target = x.getTarget();
+ JDeclaredType type = target.getEnclosingType();
+
+ if (type instanceof JClassType) {
+ JClassType cType = (JClassType) type;
+
+ if (target == enumNameMethod ||
+ target == enumToStringMethod ||
+ target == enumValueOfMethod) {
+ warn(x);
+ } else if (cType.isEnumOrSubclass() != null) {
+ if ("valueOf".equals(target.getName())) {
+ /*
+ * Check for calls to the auto-generated
EnumSubType.valueOf(String).
+ * Note, the check of the signature for the single String arg
version
+ * is to avoid flagging user-defined overloaded versions of the
+ * method, which are presumably ok.
+ */
+ List<JParameter> jParameters = target.getParams();
+ if (jParameters.size() == 1 &&
+ jParameters.get(0).getType() == stringType) {
+ warn(x);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean visit(JClassType x, Context ctx) {
+ if (x == enumType) {
+ // don't traverse into Enum class itself, don't warn on internal
method calls
+ return false;
+ }
+ return true;
+ }
+
+ private void warn(JMethodCall x) {
+ /* TODO: add a way to suppress warning with annotation if you know
what
+ * you're doing.
+ */
+ logger.log(TreeLogger.WARN, "Call to Enum method "
+ + x.getTarget().getName() + " when enum obfuscation is
enabled: "
+ + x.getSourceInfo().getFileName() + ":"
+ + x.getSourceInfo().getStartLine());
+ }
+ }
+
+ private static class EnumNameReplacer extends JModVisitor {
+
+ private final TreeLogger logger;
+ private final JProgram jprogram;
+ private final JMethod enumObfuscatedName;
+ private final JNonNullType enumType;
+
+ public EnumNameReplacer(JProgram jprogram, TreeLogger logger) {
+ this.logger = logger;
+ this.jprogram = jprogram;
+ this.enumType =
jprogram.getNonNullType(jprogram.getIndexedType("Enum"));
+ this.enumObfuscatedName =
jprogram.getIndexedMethod("Enum.obfuscatedName");
+ }
+
+ @Override
+ public void endVisit(JReturnStatement x, Context ctx) {
+ info(x);
+ JReturnStatement toReturn = new JReturnStatement(x.getSourceInfo(),
+ new JMethodCall(x.getSourceInfo(),
+ new JThisRef(x.getSourceInfo(),enumType), enumObfuscatedName));
+ ctx.replaceMe(toReturn);
+ }
+
+ public void exec() {
+ accept(jprogram.getIndexedMethod("Enum.name"));
+ }
+
+ private void info(JReturnStatement x) {
+ logger.log(TreeLogger.INFO, "Replacing Enum.name method : "
+ + x.getSourceInfo().getFileName() + ":"
+ + x.getSourceInfo().getStartLine());
+ }
+ }
+
+ public static void exec(JProgram jprogram, TreeLogger logger) {
+ new EnumNameCallChecker(jprogram, logger).accept(jprogram);
+ new EnumNameReplacer(jprogram, logger).exec();
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/EnumsWithNameObfuscationSuite.gwt.xml
Fri Sep 10 06:27:47 2010
@@ -0,0 +1,19 @@
+<!--
-->
+<!-- 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 -->
+<!-- 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. License for the specific language governing permissions
and -->
+<!-- limitations under the
License. -->
+
+<module>
+ <inherits name="com.google.gwt.dev.jjs.CompilerSuite"/>
+
+ <set-configuration-property name="compiler.enum.obfuscate.names"
value="true" />
+</module>
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/EnumsWithNameObfuscationSuite.java
Fri Sep 10 06:27:47 2010
@@ -0,0 +1,39 @@
+/*
+ * 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.dev.jjs;
+
+import com.google.gwt.dev.jjs.test.EnumsWithNameObfuscationTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * The complete compiler suite.
+ */
+public class EnumsWithNameObfuscationSuite {
+
+ public static Test suite() {
+ GWTTestSuite suite = new GWTTestSuite(
+ "Test for com.google.gwt.dev.jjs (with enum name obfuscation)");
+
+ // $JUnit-BEGIN$
+ suite.addTestSuite(EnumsWithNameObfuscationTest.class);
+ // $JUnit-END$
+
+ return suite;
+ }
+
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/test/EnumsWithNameObfuscationTest.java
Fri Sep 10 06:27:47 2010
@@ -0,0 +1,192 @@
+/*
+ * 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.dev.jjs.test;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests enum with names obfuscated functionality.
+ *
+ * Note: If running in an environment with WARN logging enabled, check to
see
+ * that calls to name(), toString(), and valueOf() below, are logged in the
+ * precompile phase.
+ */
+...@donotrunwith(Platform.Devel)
+public class EnumsWithNameObfuscationTest extends EnumsTest {
+
+ enum Fruit {
+ APPLE, BERRY, CHERRY
+ }
+
+ public String getModuleName() {
+ return "com.google.gwt.dev.jjs.EnumsWithNameObfuscationSuite";
+ }
+
+ public void testObfuscatedFruit() {
+ /*
+ * Note: Also check that the strings "APPLE" or "BERRY" never appear
+ * anywhere in the compiled output, if compilation is done with
+ * optimization and obfuscation. "CHERRY" will appear, since it
+ * is used as a string literal in the comparison tests below.
+ */
+ assertEquals(0, Fruit.APPLE.ordinal());
+ assertEquals(1, Fruit.BERRY.ordinal());
+ assertEquals(2, Fruit.CHERRY.ordinal());
+
+ assertFalse(Fruit.CHERRY.toString().equals("CHERRY"));
+ assertFalse(Fruit.CHERRY.name().equals("CHERRY"));
+
+ assertTrue(Fruit.CHERRY.toString().equals("" +
Fruit.CHERRY.ordinal()));
+ assertTrue(Fruit.CHERRY.name().equals("" + Fruit.CHERRY.ordinal()));
+
+ try {
+ Fruit.valueOf("CHERRIESONTOP");
+ fail("Enum.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Fruit.valueOf("CHERRY");
+ fail("Enum.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Enum.valueOf(Fruit.class,"CHERRIESONTOP");
+ fail("Enum.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Enum.valueOf(Fruit.class,"CHERRY");
+ fail("Enum.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+
+ @Override
+ public void testCompareTo() {
+
+ try {
+ assertTrue(Basic.A.compareTo(Basic.valueOf("A")) == 0);
+ fail("Basic.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ assertTrue(Basic.B.compareTo(Basic.A) > 0);
+ assertTrue(Basic.A.compareTo(Basic.B) < 0);
+
+ try {
+ assertTrue(Complex.A.compareTo(Complex.valueOf("A")) == 0);
+ fail("Complex.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ assertTrue(Complex.B.compareTo(Complex.A) > 0);
+ assertTrue(Complex.A.compareTo(Complex.B) < 0);
+
+ try {
+ assertTrue(Subclassing.A.compareTo(Subclassing.valueOf("A")) == 0);
+ fail("Subclassing.valueOf(), expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ assertTrue(Subclassing.B.compareTo(Subclassing.A) > 0);
+ assertTrue(Subclassing.A.compareTo(Subclassing.B) < 0);
+ }
+
+ @Override
+ public void testName() {
+ assertFalse("A".equals(Basic.A.name()));
+ assertFalse("B".equals(Basic.B.name()));
+ assertFalse("C".equals(Basic.C.name()));
+
+ assertFalse("A".equals(Complex.A.name()));
+ assertFalse("B".equals(Complex.B.name()));
+ assertFalse("C".equals(Complex.C.name()));
+
+ assertFalse("A".equals(Subclassing.A.name()));
+ assertFalse("B".equals(Subclassing.B.name()));
+ assertFalse("C".equals(Subclassing.C.name()));
+ }
+
+ @Override
+ public void testValueOf() {
+
+ try {
+ Basic.valueOf("D");
+ fail("Basic.valueOf(\"D\") -- expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Complex.valueOf("D");
+ fail("Complex.valueOf(\"D\") -- expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Subclassing.valueOf("D");
+ fail("Subclassing.valueOf(\"D\") -- expected
IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ enumValuesTest(Basic.class);
+ enumValuesTest(Complex.class);
+ enumValuesTest(Subclassing.class);
+
+ try {
+ Enum.valueOf(Basic.class, "foo");
+ fail("Passed an invalid enum constant name to Enum.valueOf;
expected "
+ + "IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Class fakeEnumClass = String.class;
+ Enum.valueOf(fakeEnumClass, "foo");
+ fail("Passed a non enum class to Enum.valueOf; expected "
+ + "IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ Class<Basic> nullEnumClass = null;
+ Enum.valueOf(nullEnumClass, "foo");
+ fail("Passed a null enum class to Enum.valueOf; expected "
+ + "NullPointerException");
+ } catch (JavaScriptException expected) {
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ Enum.valueOf(Basic.class, null);
+ fail("Passed a null enum constant to Enum.valueOf; expected "
+ + "NullPointerException");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private <T extends Enum<T>> void enumValuesTest(Class<T> enumClass) {
+ T[] constants = enumClass.getEnumConstants();
+ for (T constant : constants) {
+ assertEquals(constant, Enum.valueOf(enumClass, constant.name()));
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/user/test/com/google/gwt/emultest/java/math/MathContextTest.java
Fri Sep 10 06:27:47 2010
@@ -0,0 +1,144 @@
+/*
+ * 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.emultest.java.math;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.math.MathContext;
+import java.math.RoundingMode;
+
+/**
+ * Tests for {...@link MathContext}.
+ */
+public class MathContextTest extends EmulTestBase {
+
+ public void testMathContextSingleArgConstructor() {
+ MathContext mc1 = new MathContext("precision=16 roundingMode=CEILING");
+ assertTrue(mc1.getPrecision() == 16);
+ assertTrue(mc1.getRoundingMode().equals(RoundingMode.CEILING));
+
+ MathContext mc2 = new MathContext("precision=17 roundingMode=DOWN");
+ assertTrue(mc2.getPrecision() == 17);
+ assertTrue(mc2.getRoundingMode().equals(RoundingMode.DOWN));
+
+ MathContext mc3 = new MathContext("precision=18 roundingMode=FLOOR");
+ assertTrue(mc3.getPrecision() == 18);
+ assertTrue(mc3.getRoundingMode().equals(RoundingMode.FLOOR));
+
+ MathContext mc4 = new MathContext("precision=19
roundingMode=HALF_DOWN");
+ assertTrue(mc4.getPrecision() == 19);
+ assertTrue(mc4.getRoundingMode().equals(RoundingMode.HALF_DOWN));
+
+ MathContext mc5 = new MathContext("precision=20
roundingMode=HALF_EVEN");
+ assertTrue(mc5.getPrecision() == 20);
+ assertTrue(mc5.getRoundingMode().equals(RoundingMode.HALF_EVEN));
+
+ MathContext mc6 = new MathContext("precision=21 roundingMode=HALF_UP");
+ assertTrue(mc6.getPrecision() == 21);
+ assertTrue(mc6.getRoundingMode().equals(RoundingMode.HALF_UP));
+
+ MathContext mc7 = new MathContext("precision=22
roundingMode=UNNECESSARY");
+ assertTrue(mc7.getPrecision() == 22);
+ assertTrue(mc7.getRoundingMode().equals(RoundingMode.UNNECESSARY));
+
+ MathContext mc8 = new MathContext("precision=23 roundingMode=UP");
+ assertTrue(mc8.getPrecision() == 23);
+ assertTrue(mc8.getRoundingMode().equals(RoundingMode.UP));
+
+ // try some badly formatted args
+ try {
+ new MathContext("prcision=27 roundingMode=CEILING");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("precision=26 roundingMoe=CEILING");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("precision=25 roundingMode=CEILINGFAN");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("precision=24 roundingMode=HALF");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("precision=23 roundingMode=UPSIDEDOWN");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("precision=22roundingMode=UP");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext("");
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new MathContext(null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testMathContextConstructorEquality() {
+ MathContext mc1 = new MathContext(16,RoundingMode.CEILING);
+ MathContext mc1a = new MathContext("precision=16
roundingMode=CEILING");
+ assertTrue(mc1.equals(mc1a));
+
+ MathContext mc2 = new MathContext(17,RoundingMode.DOWN);
+ MathContext mc2a = new MathContext("precision=17 roundingMode=DOWN");
+ assertTrue(mc2.equals(mc2a));
+
+ MathContext mc3 = new MathContext(18,RoundingMode.FLOOR);
+ MathContext mc3a = new MathContext("precision=18 roundingMode=FLOOR");
+ assertTrue(mc3.equals(mc3a));
+
+ MathContext mc4 = new MathContext(19,RoundingMode.HALF_DOWN);
+ MathContext mc4a = new MathContext("precision=19
roundingMode=HALF_DOWN");
+ assertTrue(mc4.equals(mc4a));
+
+ MathContext mc5 = new MathContext(20,RoundingMode.HALF_EVEN);
+ MathContext mc5a = new MathContext("precision=20
roundingMode=HALF_EVEN");
+ assertTrue(mc5.equals(mc5a));
+
+ MathContext mc6 = new MathContext(21,RoundingMode.HALF_UP);
+ MathContext mc6a = new MathContext("precision=21
roundingMode=HALF_UP");
+ assertTrue(mc6.equals(mc6a));
+
+ MathContext mc7 = new MathContext(22,RoundingMode.UNNECESSARY);
+ MathContext mc7a = new MathContext("precision=22
roundingMode=UNNECESSARY");
+ assertTrue(mc7.equals(mc7a));
+
+ MathContext mc8 = new MathContext(23,RoundingMode.UP);
+ MathContext mc8a = new MathContext("precision=23 roundingMode=UP");
+ assertTrue(mc8.equals(mc8a));
+ }
+}
=======================================
---
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Thu Sep 9 08:24:17 2010
+++
/trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
Fri Sep 10 06:27:47 2010
@@ -34,6 +34,7 @@
import com.google.gwt.core.ext.soyc.impl.StoryRecorder;
import com.google.gwt.core.linker.SoycReportLinker;
import com.google.gwt.dev.Permutation;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
@@ -161,6 +162,10 @@
* JavaScript source.
*/
public class JavaToJavaScriptCompiler {
+
+ private static final String ENUM_NAME_OBFUSCATION_PROPERTY =
+
"compiler.enum.obfuscate.names";
+
private static class PermutationResultImpl implements PermutationResult {
private final ArtifactSet artifacts = new ArtifactSet();
private final byte[][] js;
@@ -537,7 +542,17 @@
Memory.maybeDumpMemory("AstOnly");
maybeDumpAST(jprogram);
-
+
+ // See if we should run the EnumNameObfuscator
+ if (module != null) {
+ ConfigurationProperty enumNameObfuscationProp =
+ (ConfigurationProperty)
module.getProperties().find(ENUM_NAME_OBFUSCATION_PROPERTY);
+ if (enumNameObfuscationProp != null &&
+ Boolean.parseBoolean(enumNameObfuscationProp.getValue())) {
+ EnumNameObfuscator.exec(jprogram, logger);
+ }
+ }
+
// (3) Perform Java AST normalizations.
ArtificialRescueRecorder.exec(jprogram);
FixAssignmentToUnbox.exec(jprogram);
@@ -558,7 +573,7 @@
ReplaceRebinds.exec(logger, jprogram, rpo);
// Fix up GWT.runAsync()
- if (options.isRunAsyncEnabled()) {
+ if (module != null && options.isRunAsyncEnabled()) {
ReplaceRunAsyncs.exec(logger, jprogram);
CodeSplitter.pickInitialLoadSequence(logger, jprogram,
module.getProperties());
=======================================
--- /trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml Fri Jul
23 11:50:52 2010
+++ /trunk/user/src/com/google/gwt/core/CompilerParameters.gwt.xml Fri Sep
10 06:27:47 2010
@@ -33,6 +33,15 @@
<set-property name="compiler.predeclare.cross.fragment.references"
value="false" />
+ <!--
+ Whether or not the compiler should alter Enum.name() to return
+ ordinal() as a way of obfuscating Enum field identifiers.
+ -->
+ <define-configuration-property name="compiler.enum.obfuscate.names"
+ is-multi-valued='false' />
+ <set-configuration-property name="compiler.enum.obfuscate.names"
+ value="false" />
+
<!-- From here down, the properties are unsupported and are only
available for test cases -->
<!--
@@ -53,7 +62,7 @@
JsIEBlockSizeVisitor has its usual effect.
-->
<define-configuration-property name="iframe.linker.script.chunk.size"
- is-multi-valued="false" />
+ is-multi-valued="false" />
<set-configuration-property name="iframe.linker.script.chunk.size"
value="30000" />
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java
Thu Sep 9 14:34:00 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java
Fri Sep 10 06:27:47 2010
@@ -139,9 +139,9 @@
ReturnRecord.fillKeys(returnedJso, keys);
Set<EntityProxyId> toRemove = new HashSet<EntityProxyId>();
- if (keys.contains(WriteOperation.CREATE.name())) {
+ if (keys.contains(WriteOperation.CREATE.getUnObfuscatedEnumName())) {
JsArray<ReturnRecord> newRecords =
ReturnRecord.getRecords(returnedJso,
- WriteOperation.CREATE.name());
+ WriteOperation.CREATE.getUnObfuscatedEnumName());
int length = newRecords.length();
for (int i = 0; i < length; i++) {
ReturnRecord newRecord = newRecords.get(i);
@@ -181,9 +181,9 @@
processToRemove(toRemove, WriteOperation.CREATE);
toRemove.clear();
- if (keys.contains(WriteOperation.DELETE.name())) {
+ if (keys.contains(WriteOperation.DELETE.getUnObfuscatedEnumName())) {
JsArray<ReturnRecord> deletedRecords = ReturnRecord.getRecords(
- returnedJso, WriteOperation.DELETE.name());
+ returnedJso, WriteOperation.DELETE.getUnObfuscatedEnumName());
int length = deletedRecords.length();
for (int i = 0; i < length; i++) {
ReturnRecord deletedRecord = deletedRecords.get(i);
@@ -206,9 +206,9 @@
}
}
- if (keys.contains(WriteOperation.UPDATE.name())) {
+ if (keys.contains(WriteOperation.UPDATE.getUnObfuscatedEnumName())) {
JsArray<ReturnRecord> updatedRecords = ReturnRecord.getRecords(
- returnedJso, WriteOperation.UPDATE.name());
+ returnedJso, WriteOperation.UPDATE.getUnObfuscatedEnumName());
int length = updatedRecords.length();
for (int i = 0; i < length; i++) {
ReturnRecord updatedRecord = updatedRecords.get(i);
@@ -354,7 +354,7 @@
if (recordsMap.size() == 0) {
return "";
}
- StringBuffer requestData = new StringBuffer("\"" +
writeOperation.name()
+ StringBuffer requestData = new StringBuffer("\"" +
writeOperation.getUnObfuscatedEnumName()
+ "\":[");
boolean first = true;
for (Map.Entry<EntityProxyId, ProxyJsoImpl> entry :
recordsMap.entrySet()) {
@@ -389,7 +389,7 @@
return updates;
default:
throw new IllegalStateException("unknow writeOperation "
- + writeOperation.name());
+ + writeOperation.getUnObfuscatedEnumName());
}
}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/shared/WriteOperation.java
Wed Aug 25 17:41:41 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/WriteOperation.java
Fri Sep 10 06:27:47 2010
@@ -24,5 +24,19 @@
* The write operation enum used in DeltaValueStore.
*/
public enum WriteOperation {
- CREATE, UPDATE, DELETE
-}
+ CREATE("CREATE"),
+ UPDATE("UPDATE"),
+ DELETE("DELETE");
+
+ // use an unObfuscatedEnumName field to bypass the implicit name()
method,
+ // to be safe in the case enum name obfuscation is enabled.
+ private final String unObfuscatedEnumName;
+
+ private WriteOperation(String unObfuscatedEnumName) {
+ this.unObfuscatedEnumName = unObfuscatedEnumName;
+ }
+
+ public String getUnObfuscatedEnumName() {
+ return this.unObfuscatedEnumName;
+ }
+}
=======================================
--- /trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
Wed Oct 28 09:10:53 2009
+++ /trunk/user/src/com/google/gwt/rpc/client/ast/IdentityValueCommand.java
Fri Sep 10 06:27:47 2010
@@ -18,7 +18,7 @@
/**
* Represents a hierarchy of value types that must maintain distinct object
* identity on the client. This type finalizes <code>equals</code> and
- * <code>hashCode</code> to give subtypes identity equality sematics.
+ * <code>hashCode</code> to give subtypes identity equality semantics.
*/
public abstract class IdentityValueCommand extends ValueCommand {
@Override
=======================================
--- /trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
Wed Oct 28 09:10:53 2009
+++ /trunk/user/src/com/google/gwt/rpc/client/impl/SimplePayloadSink.java
Fri Sep 10 06:27:47 2010
@@ -73,10 +73,11 @@
@Override
public void endVisit(EnumValueCommand x, Context ctx) {
- // ETypeSeedName~"9~FieldName
+ // ETypeSeedName~IOrdinal~
if (appendIdentity(x)) {
appendTypedData(ENUM_TYPE,
x.getValue().getDeclaringClass().getName());
- accept(new StringValueCommand(x.getValue().name()));
+ // use ordinal (and not name), since name might have been
obfuscated
+ appendTypedData(INT_TYPE, String.valueOf(x.getValue().ordinal()));
}
}
=======================================
--- /trunk/user/src/com/google/gwt/rpc/server/SimplePayloadDecoder.java Wed
Oct 28 09:10:53 2009
+++ /trunk/user/src/com/google/gwt/rpc/server/SimplePayloadDecoder.java Fri
Sep 10 06:27:47 2010
@@ -198,16 +198,22 @@
break;
}
case ENUM_TYPE: {
- // ETypeSeedName~"9~FieldName
+ // ETypeSeedName~IOrdinal~
EnumValueCommand x = new EnumValueCommand();
push(x);
- String name = readCommand(StringValueCommand.class).getValue();
+
+ // use ordinal (and not name), since name might have been
obfuscated
+ int ordinal = readCommand(IntValueCommand.class).getValue();
@SuppressWarnings("unchecked")
Class<? extends Enum> clazz =
findClass(token).asSubclass(Enum.class);
- @SuppressWarnings("unchecked")
- Enum<?> enumValue = Enum.valueOf(clazz, name);
- x.setValue(enumValue);
+
+ /*
+ * TODO: Note this approach could be prone to subtle corruption or
+ * an ArrayOutOfBoundsException if the client and server have
drifted.
+ */
+ Enum<?> enumConstants[] = clazz.getEnumConstants();
+ x.setValue(enumConstants[ordinal]);
break;
}
case ARRAY_TYPE: {
=======================================
--- /trunk/user/super/com/google/gwt/emul/java/lang/Enum.java Fri May 28
09:34:24 2010
+++ /trunk/user/super/com/google/gwt/emul/java/lang/Enum.java Fri Sep 10
06:27:47 2010
@@ -39,7 +39,7 @@
T[] enumConstants) {
JavaScriptObject result = JavaScriptObject.createObject();
for (T value : enumConstants) {
- put0(result, ":" + value.name, value);
+ put0(result, ":" + value.name(), value);
}
return result;
}
@@ -114,6 +114,10 @@
public final String name() {
return name;
}
+
+ public final String obfuscatedName() {
+ return "" + ordinal;
+ }
public final int ordinal() {
return ordinal;
@@ -121,7 +125,6 @@
@Override
public String toString() {
- return name;
- }
-
-}
+ return name();
+ }
+}
=======================================
--- /trunk/user/super/com/google/gwt/emul/java/math/MathContext.java Thu
Feb 25 10:33:31 2010
+++ /trunk/user/super/com/google/gwt/emul/java/math/MathContext.java Fri
Sep 10 06:27:47 2010
@@ -162,6 +162,9 @@
* or if the precision specified is < 0.
*/
public MathContext(String val) {
+ if (val == null) {
+ throw new NullPointerException("null string");
+ }
char[] charVal = val.toCharArray();
int i; // Index of charVal
int j; // Index of chRoundingMode
@@ -219,8 +222,13 @@
// math.0E=bad string format
throw new IllegalArgumentException("bad string format");
//$NON-NLS-1$
}
+
// Parsing the value for "roundingMode"...
- this.roundingMode = RoundingMode.valueOf(String.valueOf(charVal, i,
+ /*
+ * don't use implicit calls to RoundingMode.valueOf here, since it
will break
+ * if enum name obfuscation is enabled.
+ */
+ this.roundingMode =
RoundingMode.valueOfExplicit(String.valueOf(charVal, i,
charVal.length - i));
}
@@ -237,7 +245,8 @@
@Override
public boolean equals(Object x) {
return ((x instanceof MathContext)
- && (((MathContext) x).getPrecision() == precision) &&
(((MathContext) x).getRoundingMode() == roundingMode));
+ && (((MathContext) x).getPrecision() == precision)
+ && (((MathContext) x).getRoundingMode() == roundingMode));
}
/**
=======================================
--- /trunk/user/super/com/google/gwt/emul/java/math/RoundingMode.java Mon
Jun 7 12:20:31 2010
+++ /trunk/user/super/com/google/gwt/emul/java/math/RoundingMode.java Fri
Sep 10 06:27:47 2010
@@ -92,7 +92,18 @@
*/
UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);
-
+ /**
+ * Some constant char arrays for optimized comparisons
+ */
+ private static final char[] chCEILING = {'C','E','I','L','I','N','G'};
+ private static final char[] chDOWN = {'D','O','W','N'};
+ private static final char[] chFLOOR = {'F','L','O','O','R'};
+ private static final char[] chHALF_DOWN =
{'H','A','L','F','_','D','O','W','N'};
+ private static final char[] chHALF_EVEN =
{'H','A','L','F','_','E','V','E','N'};
+ private static final char[] chHALF_UP = {'H','A','L','F','_','U','P'};
+ private static final char[] chUNNECESSARY =
{'U','N','N','E','C','E','S','S','A','R','Y'};
+ private static final char[] chUP = {'U','P'};
+
/**
* Converts rounding mode constants from class {...@code BigDecimal} into
{...@code
* RoundingMode} values.
@@ -123,7 +134,76 @@
throw new IllegalArgumentException("Invalid rounding mode");
//$NON-NLS-1$
}
}
-
+
+ /**
+ * Bypasses calls to the implicit valueOf(String) method, which will
break
+ * if enum name obfuscation is enabled. This should be package visible
only.
+ *
+ * @param mode rounding mode string as defined in class {...@code
BigDecimal}
+ * @return corresponding rounding mode object
+ */
+ static RoundingMode valueOfExplicit(String mode) {
+ /*
+ * Note this is optimized to avoid multiple String compares,
+ * using specific knowledge of the set of allowed enum constants.
+ */
+
+ if (mode == null) {
+ throw new NullPointerException();
+ }
+
+ char[] modeChars = mode.toCharArray();
+ int len = modeChars.length;
+ if (len < chUP.length || len > chUNNECESSARY.length) {
+ throw new IllegalArgumentException();
+ }
+
+ char[] targetChars = null;
+ RoundingMode target = null;
+ if (modeChars[0] == 'C') {
+ target = RoundingMode.CEILING;
+ targetChars = chCEILING;
+ } else if (modeChars[0] == 'D') {
+ target = RoundingMode.DOWN;
+ targetChars = chDOWN;
+ } else if (modeChars[0] == 'F') {
+ target = RoundingMode.FLOOR;
+ targetChars = chFLOOR;
+ } else if (modeChars[0] == 'H') {
+ if (len > 6) {
+ if (modeChars[5] == 'D') {
+ target = RoundingMode.HALF_DOWN;
+ targetChars = chHALF_DOWN;
+ } else if (modeChars[5] == 'E') {
+ target = RoundingMode.HALF_EVEN;
+ targetChars = chHALF_EVEN;
+ } else if (modeChars[5] == 'U') {
+ target = RoundingMode.HALF_UP;
+ targetChars = chHALF_UP;
+ }
+ }
+ } else if (modeChars[0] == 'U') {
+ if (modeChars[1] == 'P') {
+ target = RoundingMode.UP;
+ targetChars = chUP;
+ } else if (modeChars[1] == 'N') {
+ target = RoundingMode.UNNECESSARY;
+ targetChars = chUNNECESSARY;
+ }
+ }
+
+ if (target != null && len == targetChars.length) {
+ int i;
+ for (i = 1; i < len && modeChars[i] == targetChars[i]; i++) {
+ }
+ if (i == len) {
+ return target;
+ }
+ }
+
+ throw new IllegalArgumentException();
+ }
+
/**
* Set the old constant.
* @param rm unused
=======================================
--- /trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.java Mon Feb 1
07:58:03 2010
+++ /trunk/user/test/com/google/gwt/dev/jjs/CompilerSuite.java Fri Sep 10
06:27:47 2010
@@ -26,6 +26,7 @@
import com.google.gwt.dev.jjs.test.CoverageTest;
import com.google.gwt.dev.jjs.test.EnhancedForLoopTest;
import com.google.gwt.dev.jjs.test.EnumsTest;
+import com.google.gwt.dev.jjs.test.EnumsWithNameObfuscationTest;
import com.google.gwt.dev.jjs.test.GenericCastTest;
import com.google.gwt.dev.jjs.test.HostedTest;
import com.google.gwt.dev.jjs.test.InitialLoadSequenceTest;
@@ -72,6 +73,7 @@
suite.addTestSuite(CoverageTest.class);
suite.addTestSuite(EnhancedForLoopTest.class);
suite.addTestSuite(EnumsTest.class);
+ suite.addTestSuite(EnumsWithNameObfuscationTest.class);
suite.addTestSuite(GenericCastTest.class);
suite.addTestSuite(HostedTest.class);
suite.addTestSuite(InitialLoadSequenceTest.class);
=======================================
--- /trunk/user/test/com/google/gwt/emultest/EmulSuite.java Fri Aug 13
12:36:25 2010
+++ /trunk/user/test/com/google/gwt/emultest/EmulSuite.java Fri Sep 10
06:27:47 2010
@@ -30,6 +30,7 @@
import com.google.gwt.emultest.java.lang.StringBufferTest;
import com.google.gwt.emultest.java.lang.StringTest;
import com.google.gwt.emultest.java.lang.SystemTest;
+import com.google.gwt.emultest.java.math.MathContextTest;
import com.google.gwt.emultest.java.math.RoundingModeTest;
import com.google.gwt.emultest.java.security.MessageDigestTest;
import com.google.gwt.emultest.java.sql.SqlDateTest;
@@ -88,6 +89,7 @@
// BigDecimal is tested in {...@link BigDecimalSuite}
// BigInteger is tested in {...@link BigIntegerSuite}
suite.addTestSuite(RoundingModeTest.class);
+ suite.addTestSuite(MathContextTest.class);
//-- java.util
suite.addTestSuite(ApacheMapTest.class);
=======================================
---
/trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
Tue Sep 7 10:09:37 2010
+++
/trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java
Fri Sep 10 06:27:47 2010
@@ -82,7 +82,7 @@
valueStore, requestFactory);
String json = deltaValueStore.toJson();
JSONObject jsonObject = (JSONObject) JSONParser.parseLenient(json);
- assertFalse(jsonObject.containsKey(WriteOperation.CREATE.name()));
+
assertFalse(jsonObject.containsKey(WriteOperation.CREATE.getUnObfuscatedEnumName()));
}
public void testCreateWithSet() {
@@ -152,19 +152,19 @@
assertTrue(deltaValueStore.isChanged());
String jsonString = deltaValueStore.toJson();
JSONObject jsonObject = (JSONObject)
JSONParser.parseLenient(jsonString);
- assertFalse(jsonObject.containsKey(WriteOperation.DELETE.name()));
- assertTrue(jsonObject.containsKey(WriteOperation.CREATE.name()));
- assertTrue(jsonObject.containsKey(WriteOperation.UPDATE.name()));
+
assertFalse(jsonObject.containsKey(WriteOperation.DELETE.getUnObfuscatedEnumName()));
+
assertTrue(jsonObject.containsKey(WriteOperation.CREATE.getUnObfuscatedEnumName()));
+
assertTrue(jsonObject.containsKey(WriteOperation.UPDATE.getUnObfuscatedEnumName()));
JSONArray createOperationArray = jsonObject.get(
- WriteOperation.CREATE.name()).isArray();
+ WriteOperation.CREATE.getUnObfuscatedEnumName()).isArray();
assertEquals(1, createOperationArray.size());
assertEquals("harry", createOperationArray.get(0).isObject().get(
SIMPLE_FOO_CLASS_NAME).isObject().get(
SimpleFooProxy.userName.getName()).isString().stringValue());
JSONArray updateOperationArray = jsonObject.get(
- WriteOperation.UPDATE.name()).isArray();
+ WriteOperation.UPDATE.getUnObfuscatedEnumName()).isArray();
assertEquals(1, updateOperationArray.size());
assertEquals("bovik", updateOperationArray.get(0).isObject().get(
SIMPLE_FOO_CLASS_NAME).isObject().get(
@@ -189,13 +189,14 @@
JSONObject jsonObject = (JSONObject)
JSONParser.parseLenient(jsonString);
for (WriteOperation writeOperation : WriteOperation.values()) {
if (writeOperation != currentWriteOperation) {
- assertFalse(jsonObject.containsKey(writeOperation.name()));
+
assertFalse(jsonObject.containsKey(writeOperation.getUnObfuscatedEnumName()));
} else {
- assertTrue(jsonObject.containsKey(writeOperation.name()));
+
assertTrue(jsonObject.containsKey(writeOperation.getUnObfuscatedEnumName()));
}
}
- JSONArray writeOperationArray =
jsonObject.get(currentWriteOperation.name()).isArray();
+ JSONArray writeOperationArray =
+
jsonObject.get(currentWriteOperation.getUnObfuscatedEnumName()).isArray();
assertEquals(1, writeOperationArray.size());
JSONObject proxyWithName = writeOperationArray.get(0).isObject();
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors