blob: 5215aeff2ccff0e84881839b93233a125ed6659b [file] [log] [blame]
/*
* 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.impl;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JType;
/**
* Test for {@link TempLocalVisitor}.
*/
public class TempLocalVisitorTest extends JJSTestBase {
/**
* Replaces expressions with assignment-to-temp. Does not replace lvalues, or
* the top level expression in an expression statement.
*/
private static final class AlwaysReplacer extends TempLocalVisitor {
/**
* Don't bother replacing entire JExpressionStatements with a temp.
*/
private JExpression dontBother;
@Override
public boolean visit(JExpressionStatement x, Context ctx) {
dontBother = x.getExpr();
return super.visit(x, ctx);
}
@Override
public void endVisit(JExpression x, Context ctx) {
if (x != dontBother && !ctx.isLvalue()) {
SourceInfo info = x.getSourceInfo();
JType type = x.getType();
JLocal local = createTempLocal(info, type);
ctx.replaceMe(new JBinaryOperation(info, type, JBinaryOperator.ASG,
new JLocalRef(info, local), x));
}
}
}
private final class Result {
private final String optimized;
private final String returnType;
private final String userCode;
public Result(String returnType, String userCode, String optimized) {
this.returnType = returnType;
this.userCode = userCode;
this.optimized = optimized;
}
public void into(String expected) throws UnableToCompleteException {
JProgram program = compileSnippet(returnType, expected);
expected = getMainMethodSource(program);
assertEquals(userCode, expected, optimized);
}
}
public void testBasic() throws Exception {
assertTransform("int i = 3;").into("int $t0; int i = $t0 = 3;");
}
public void testExisting() throws Exception {
assertTransform("int $t0; int i = $t0 = 3;").into(
"int $t0; int $t1; int $t2; int i = $t2 = $t0 = $t1 = 3;");
}
public void testForStatement() throws Exception {
StringBuilder original = new StringBuilder();
original.append("for (int i = 0; true; i += 1);");
StringBuilder expected = new StringBuilder();
/*
* TODO(scottb): technically $t1 and $t2 could be part of the for
* statement's initializer list.
*/
expected.append("boolean $t1;");
expected.append("int $t2;");
expected.append("for (int $t0, i = $t0 = 0; $t1 = true; i += $t2 = 1);");
assertTransform(original.toString()).into(expected.toString());
}
public void testForStatementScoping() throws Exception {
StringBuilder original = new StringBuilder();
original.append("boolean f = false;");
original.append("for (int i = 3; f; );");
original.append("int j = 4;");
StringBuilder expected = new StringBuilder();
expected.append("boolean $t0;");
expected.append("boolean f = $t0 = false;");
expected.append("boolean $t2;");
expected.append("for (int $t1, i = $t1 = 3; $t2 = f; );");
/*
* TODO(scottb): technically we could reuse $t1 here as a minor improvement.
*/
expected.append("int $t3; int j = $t3 = 4;");
assertTransform(original.toString()).into(expected.toString());
}
public void testNested() throws Exception {
assertTransform("{ int i = 3; } ").into("{ int $t0; int i = $t0 = 3; }");
}
public void testNestedPost() throws Exception {
StringBuilder original = new StringBuilder();
original.append("int i = 3;");
original.append("{ int j = 4; }");
StringBuilder expected = new StringBuilder();
expected.append("int $t0; int i = $t0 = 3;");
expected.append("{ int $t1; int j = $t1 = 4; }");
assertTransform(original.toString()).into(expected.toString());
}
public void testNestedPre() throws Exception {
StringBuilder original = new StringBuilder();
original.append("{ int i = 3; }");
original.append("int j = 4;");
StringBuilder expected = new StringBuilder();
expected.append("{ int $t0; int i = $t0 = 3; }");
expected.append("int $t1; int j = $t1 = 4;");
assertTransform(original.toString()).into(expected.toString());
}
public void testReuse() throws Exception {
StringBuilder original = new StringBuilder();
original.append("{ int i = 3; }");
original.append("{ int j = 4; }");
StringBuilder expected = new StringBuilder();
expected.append("{ int $t0; int i = $t0 = 3; }");
expected.append("{ int $t0; int j = $t0 = 4; }");
assertTransform(original.toString()).into(expected.toString());
}
public void testReuseTwice() throws Exception {
StringBuilder original = new StringBuilder();
original.append("{ int i = 3; int j = 4; }");
original.append("{ int i = 3; int j = 4; }");
StringBuilder expected = new StringBuilder();
expected.append("{ int $t0; int i = $t0 = 3; int $t1; int j = $t1 = 4; }");
expected.append("{ int $t0; int i = $t0 = 3; int $t1; int j = $t1 = 4; }");
assertTransform(original.toString()).into(expected.toString());
}
public void testVeryComplex() throws Exception {
StringBuilder original = new StringBuilder();
original.append("int a = 0;");
original.append("{ int i = 3; int j = 4; }");
original.append("int b = 1;");
original.append("{ int i = 3; int j = 4; }");
original.append("int c = 2;");
StringBuilder expected = new StringBuilder();
expected.append("int $t0; int a = $t0 = 0;");
expected.append("{ int $t1; int i = $t1 = 3; int $t2; int j = $t2 = 4; }");
expected.append("int $t3; int b = $t3 = 1;");
expected.append("{ int $t1; int i = $t1 = 3; int $t2; int j = $t2 = 4; }");
expected.append("int $t4; int c = $t4 = 2;");
assertTransform(original.toString()).into(expected.toString());
}
private Result assertTransform(String codeSnippet)
throws UnableToCompleteException {
JProgram program = compileSnippet("void", codeSnippet);
JMethod mainMethod = findMainMethod(program);
new AlwaysReplacer().accept(mainMethod);
return new Result("void", codeSnippet, mainMethod.getBody().toSource());
}
}