Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -330,30 +330,38 @@ public class JavaResourceBase {
createMockJavaResource("java.lang.String",
"package java.lang;",
"import java.io.Serializable;",
"import javaemul.internal.annotations.ConstantFoldCandidate;",
"import javaemul.internal.annotations.SpecializeMethod;",
"public final class String implements Comparable<String>, CharSequence, Serializable {",
" public String() { }",
" public String(char c) { }",
" public String(String s) { }",
" public static String _String() { return \"\"; }",
" public static String _String(char c) { return \"\" + c; }",
" public static String _String(String s) { return s; }",
// " public static String _String() { return \"\"; }",
// " public static String _String(char c) { return \"\" + c; }",
// " public static String _String(String s) { return s; }",
" @ConstantFoldCandidate",
" public char charAt(int index) { return 'a'; }",
" public int compareTo(String other) { return -1; }",
" @ConstantFoldCandidate",
" @SpecializeMethod(params = String.class, target = \"equals\")",
" public boolean equals(Object other) {",
" return (other instanceof String) && equals((String) other);",
" }",
" @ConstantFoldCandidate",
" private native boolean equals(String obj) /*-{ return false; }-*/;",
" @ConstantFoldCandidate",
" public boolean equalsIgnoreCase(String str) { return false; }",
" @ConstantFoldCandidate",
" public int length() { return 0; }",
" public static String valueOf(int i) { return \"\" + i; }",
" public static String valueOf(char c) { return \"\" + c; }",
" @ConstantFoldCandidate",
" public int hashCode() { return 0; }",
" public String replace(char c1, char c2) { return null; }",
" public boolean startsWith(String str) { return false; }",
" public native String substring(int start, int len) /*-{ return \"\"; }-*/;",
" public String toLowerCase() { return null; }",
" @ConstantFoldCandidate",
" public String toString() { return this; }",
" public static String valueOf(boolean b) { return null; }",
"}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ private void optimizeJava() throws InterruptedException {
if (shouldOptimize()) {
optimizeJavaToFixedPoint();
RemoveEmptySuperCalls.exec(jprogram);
RemoveSpecializations.exec(jprogram);
MethodInliner.exec(jprogram);
}
}

Expand All @@ -551,7 +553,6 @@ private void optimizeJs(Set<JsNode> inlinableJsFunctions) throws InterruptedExce
private void postNormalizationOptimizeJava() {
try (SimpleEvent ignored = new SimpleEvent("Post-Normalization Java Optimizers")) {
if (shouldOptimize()) {
RemoveSpecializations.exec(jprogram);
Pruner.exec(jprogram, false, OptimizerContext.NULL_OPTIMIZATION_CONTEXT);
// Last Java optimization step, update type oracle accordingly.
jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes());
Expand Down
10 changes: 10 additions & 0 deletions dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public int compare(JMethod m1, JMethod m2) {
private boolean preventDevirtualization = false;
private boolean hasSideEffects = true;
private boolean defaultMethod = false;
private boolean allowConstantFolding = false;
private boolean syntheticAccidentalOverride = false;
private Set<String> suppressedWarnings;

Expand Down Expand Up @@ -322,6 +323,15 @@ public boolean isJsMethodVarargs() {
JParameter lastParameter = Iterables.getLast(getParams());
return lastParameter.isVarargs();
}

public boolean isConstantFoldingAllowed() {
return allowConstantFolding;
}

public void setAllowConstantFolding(boolean allowConstantFolding) {
this.allowConstantFolding = allowConstantFolding;
}

/**
* AST representation of @SpecializeMethod.
*/
Expand Down
93 changes: 58 additions & 35 deletions dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
Original file line number Diff line number Diff line change
Expand Up @@ -487,10 +487,8 @@ public void endVisit(JMethodCall x, Context ctx) {

// Normal optimizations.
JDeclaredType targetType = target.getEnclosingType();
if (targetType == program.getTypeJavaLangString() ||
(instance != null &&
instance.getType().getUnderlyingType() == program.getTypeJavaLangString())) {
tryOptimizeStringCall(x, ctx, target);
if (target.isConstantFoldingAllowed()) {
tryFoldMethodCall(x, ctx, target);
} else if (JProgram.isClinit(target)) {
// Eliminate the call if the target is now empty.
if (!targetType.hasClinit()) {
Expand Down Expand Up @@ -1832,9 +1830,10 @@ private JLiteral tryGetConstant(JVariableRef x) {
/**
* Replace String methods having literal args with the static result.
*/
private void tryOptimizeStringCall(JMethodCall x, Context ctx, JMethod method) {
private void tryFoldMethodCall(JMethodCall x, Context ctx, JMethod method) {

if (method.getType() == program.getTypeVoid()) {
// TODO should warn, this shouldn't even be decorated
return;
}

Expand All @@ -1851,23 +1850,40 @@ private void tryOptimizeStringCall(JMethodCall x, Context ctx, JMethod method) {
? x.getArgs().subList(1, x.getArgs().size())
: x.getArgs();

// Handle toString specially to make sure it is a noop on non null string objects.
if (method.getName().endsWith("toString")) {
// replaces s.toString() with s
if (!instance.getType().canBeNull()) {
// Only replace when it known to be non null, otherwise it should follow the normal path
// and throw an NPE if null at runtime.
ctx.replaceMe(instance);
}
Class<?> methodEnclosingType;
try {
methodEnclosingType = Class.forName(method.getEnclosingType().getName());
} catch (ClassNotFoundException e) {
// Fall back to built-in types only
methodEnclosingType = classObjectForType(method.getEnclosingType());
}
if (methodEnclosingType == null) {
// Can't find a type to invoke the method on, give up
return;
}

Object instanceLiteral = tryTranslateLiteral(instance, String.class);
method = isStaticImplMethod ? program.instanceMethodForStaticImpl(method) : method;
final Object instanceLiteral;
if (instance != null) {
instanceLiteral = tryTranslateLiteral(instance, methodEnclosingType);
method = isStaticImplMethod ? program.instanceMethodForStaticImpl(method) : method;

// Handle toString specially to make sure it is a noop on non-null string objects.
if (method.getName().equals("toString") && method.getOriginalParamTypes().isEmpty()) {
// replaces s.toString() with s
if (!instance.getType().canBeNull() && instance.getType() == program.getTypeJavaLangString().strengthenToNonNull()) {
// Only replace when it is known to be non-null, otherwise it should follow the normal path
// and throw an NPE if null at runtime.
ctx.replaceMe(instance);
}
return;
}

if (instanceLiteral == null && !method.isStatic()) {
// Instance method on an instance that was not a literal.
return;
if (instanceLiteral == null && !method.isStatic()) {
// Instance method on an instance that was not a literal.
return;
}
} else {
instanceLiteral = null;
}

// Extract constant values from arguments or bail out if not possible, and also extract the
Expand All @@ -1885,20 +1901,20 @@ private void tryOptimizeStringCall(JMethodCall x, Context ctx, JMethod method) {
}

// Finally invoke the method statically.
JLiteral resultValue = staticallyInvokeStringMethod(
method.getName(), instanceLiteral, parametersClasses, argumentConstantValues);
JLiteral resultValue = staticallyInvokeMethod(
method.getName(), methodEnclosingType, instanceLiteral, parametersClasses, argumentConstantValues);
if (resultValue != null) {
ctx.replaceMe(resultValue);
}
}

private JLiteral staticallyInvokeStringMethod(
String methodName, Object instance, Class<?>[] parameterClasses, Object[] argumentValues) {
Method actualMethod = getMethod(methodName, String.class, parameterClasses);
private JLiteral staticallyInvokeMethod(
String methodName, Class<?> methodEnclosingType, Object instance, Class<?>[] parameterClasses, Object[] argumentValues) {
Method actualMethod = getMethod(methodName, methodEnclosingType, parameterClasses);
if (actualMethod == null) {
// Convert all parameters types to Object to find a more generic applicable method.
Arrays.fill(parameterClasses, Object.class);
actualMethod = getMethod(methodName, String.class, parameterClasses);
actualMethod = getMethod(methodName, methodEnclosingType, parameterClasses);
}
if (actualMethod == null) {
return null;
Expand Down Expand Up @@ -1979,31 +1995,28 @@ private Object tryTranslateLiteral(JExpression maybeLiteral, Class<?> type) {
}
// TODO: make this way better by a mile
if (type == boolean.class && maybeLiteral instanceof JBooleanLiteral) {
return Boolean.valueOf(((JBooleanLiteral) maybeLiteral).getValue());
return ((JBooleanLiteral) maybeLiteral).getValue();
}
if (type == char.class && maybeLiteral instanceof JCharLiteral) {
return Character.valueOf(((JCharLiteral) maybeLiteral).getValue());
return ((JCharLiteral) maybeLiteral).getValue();
}
if (type == double.class && maybeLiteral instanceof JFloatLiteral) {
return new Double(((JFloatLiteral) maybeLiteral).getValue());
return ((JFloatLiteral) maybeLiteral).getValue();
}
if (type == double.class && maybeLiteral instanceof JDoubleLiteral) {
return new Double(((JDoubleLiteral) maybeLiteral).getValue());
return ((JDoubleLiteral) maybeLiteral).getValue();
}
if (type == int.class && maybeLiteral instanceof JIntLiteral) {
return Integer.valueOf(((JIntLiteral) maybeLiteral).getValue());
return ((JIntLiteral) maybeLiteral).getValue();
}
if (type == long.class && maybeLiteral instanceof JLongLiteral) {
return Long.valueOf(((JLongLiteral) maybeLiteral).getValue());
return ((JLongLiteral) maybeLiteral).getValue();
}
if (type == String.class && maybeLiteral instanceof JStringLiteral) {
return ((JStringLiteral) maybeLiteral).getValue();
}
if (type == Object.class) {
// We already know it is a JValueLiteral instance
return ((JValueLiteral) maybeLiteral).getValueObj();
}
return null;
// We already know it is a JValueLiteral instance
return ((JValueLiteral) maybeLiteral).getValueObj();
}
}

Expand Down Expand Up @@ -2072,17 +2085,27 @@ private static Set<JMethod> affectedMethods(OptimizerContext optimizerCtx) {

public DeadCodeElimination(JProgram program) {
this.program = program;
// TODO try to start with the root types were concerned with and work upwards?
classObjectsByType = new ImmutableMap.Builder()
.put(program.getTypeJavaLangObject(), Object.class)
.put(program.getTypeJavaLangString(), String.class)
.put(program.getIndexedType("CharSequence"), CharSequence.class)
.put(program.getTypePrimitiveBoolean(), boolean.class)
.put(program.getTypePrimitiveBoolean().getWrapperTypeName(), Boolean.class)
.put(program.getTypePrimitiveByte(), byte.class)
.put(program.getTypePrimitiveByte().getWrapperTypeName(), Byte.class)
.put(program.getTypePrimitiveChar(), char.class)
.put(program.getTypePrimitiveChar().getWrapperTypeName(), Character.class)
.put(program.getTypePrimitiveDouble(), double.class)
.put(program.getTypePrimitiveDouble().getWrapperTypeName(), Double.class)
.put(program.getTypePrimitiveFloat(), float.class)
.put(program.getTypePrimitiveFloat().getWrapperTypeName(), Float.class)
.put(program.getTypePrimitiveInt(), int.class)
.put(program.getTypePrimitiveInt().getWrapperTypeName(), Integer.class)
.put(program.getTypePrimitiveLong(), long.class)
.put(program.getTypePrimitiveLong().getWrapperTypeName(), Long.class)
.put(program.getTypePrimitiveShort(), short.class)
.put(program.getTypePrimitiveShort().getWrapperTypeName(), Short.class)
.build();
}

Expand Down
8 changes: 8 additions & 0 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4479,6 +4479,7 @@ private void createMethod(AbstractMethodDeclaration x) {

private void processAnnotations(Annotation[] annotations, JMethod method) {
maybeAddMethodSpecialization(annotations, method);
maybeMarkConstantFoldCandidate(annotations, method);
maybeSetInliningMode(annotations, method);
maybeSetHasNoSideEffects(annotations, method);
JsInteropUtil.maybeSetJsInteropProperties(method, shouldExport(method), annotations);
Expand Down Expand Up @@ -4541,6 +4542,13 @@ private void maybeAddMethodSpecialization(Annotation[] annotations, JMethod meth
method.setSpecialization(paramTypes, returnsType, targetMethod);
}

private void maybeMarkConstantFoldCandidate(Annotation[] annotations, JMethod method) {
if (JdtUtil.getAnnotationByName(
annotations, "javaemul.internal.annotations.ConstantFoldCandidate") != null) {
method.setAllowConstantFolding(true);
}
}

private void createParameter(SourceInfo info, LocalVariableBinding binding, boolean isVarargs,
JMethod method, Annotation... annotations) {
createParameter(info, binding, intern(binding.name), method, isVarargs, annotations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ public boolean visit(JMethod x, Context ctx) {
.getAccess());
newMethod.setInliningMode(x.getInliningMode());
newMethod.setHasSideEffects(x.hasSideEffects());
newMethod.setAllowConstantFolding(x.isConstantFoldingAllowed());
newMethod.setSynthetic();
newMethod.addThrownExceptions(x.getThrownExceptions());
if (x.isJsOverlay()) {
Expand Down
5 changes: 5 additions & 0 deletions dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ public void endVisit(JMethodCall x, Context ctx) {
return;
}

if (method.isConstantFoldingAllowed()) {
// Can't inline this method while it might still be constant-folded.
return;
}

JMethod.Specialization specialization = getCurrentMethod().getSpecialization();
// If we have a specialization, don't inline that away - specializations must be called
// so they aren't pruned or type tightened into uselessness.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ public RemoveSpecializations(JProgram program) {

@Override
public boolean visit(JMethod x, Context ctx) {
boolean modified = false;
if (x.getSpecialization() != null) {
x.removeSpecialization();
modified = true;
}
if (x.isConstantFoldingAllowed()) {
x.setAllowConstantFolding(false);
modified = true;
}
if (modified) {
numMods++;
}

Expand Down
6 changes: 3 additions & 3 deletions user/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<property name="emma.merged.out" value="${junit.out}/emma-coverage"/>

<property name="gwt.junit.testcase.web.includes" value="${gwt.junit.testcase.includes}" />
<property name="gwt.junit.testcase.web.excludes" value="**/*JreSuite.class,**/OptimizedOnly*" />
<property name="gwt.junit.testcase.web.excludes" value="**/*JreSuite.class" />

<!-- Disable legacy dev mode test on Java 9 and above -->
<property name="test.dev.htmlunit.disable" value="true" />
Expand Down Expand Up @@ -432,7 +432,7 @@
value="${test.args.web.selenium} -draftCompile"/>
<fileset id="test.draft.selenium.tests" dir="${javac.junit.out}"
includes="${gwt.junit.testcase.web.includes}"
excludes="${gwt.junit.testcase.web.excludes}"/>
excludes="${gwt.junit.testcase.web.excludes},**/OptimizedOnly*"/>
<gwt.junit test.name="test.draft.selenium"
test.args='${test.draft.selenium.args}'
test.jvmargs="${test.jvmargs}"
Expand Down Expand Up @@ -523,7 +523,7 @@
unless="test.draft.htmlunit.disable">
<fileset id="test.draft.htmlunit.tests" dir="${javac.junit.out}"
includes="${gwt.junit.testcase.web.includes}"
excludes="${gwt.junit.testcase.web.excludes}"/>
excludes="${gwt.junit.testcase.web.excludes},**/OptimizedOnly*"/>
<gwt.junit test.name="test.draft.htmlunit"
test.args="${test.args.web.htmlunit} -draftCompile"
test.jvmargs="${test.jvmargs}"
Expand Down
2 changes: 2 additions & 0 deletions user/super/com/google/gwt/emul/java/lang/Integer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package java.lang;

import javaemul.internal.JsUtils;
import javaemul.internal.annotations.ConstantFoldCandidate;
import javaemul.internal.annotations.HasNoSideEffects;

/**
Expand Down Expand Up @@ -81,6 +82,7 @@ public static int compare(int x, int y) {
}
}

@ConstantFoldCandidate
public static Integer decode(String s) throws NumberFormatException {
return Integer.valueOf(__decodeAndValidateInt(s, MIN_VALUE, MAX_VALUE));
}
Expand Down
Loading