Skip to content

Commit

Permalink
Updates NilChkResolver with several fixes:
Browse files Browse the repository at this point in the history
- Method calls remove any assumption about the nullness of non-local variable, since the method call might modify such variables.
- Improves the scope stack for better management of various control flow patterns.
- Fixes handling of break, continue, return and exception throwing.
- Uses the result of null checks within loop conditions.
Extracts LabelRewriter from Rewriter and run this translation after NilChkResolver.
Updates EnhancedForRewriter to handle loop labels.
	Change on 2016/02/18 by kstanger <kstanger@google.com>
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=114972263
  • Loading branch information
kstanger authored and tomball committed Feb 18, 2016
1 parent 0f24e76 commit 5b19426
Show file tree
Hide file tree
Showing 12 changed files with 870 additions and 269 deletions.
1 change: 1 addition & 0 deletions translator/Makefile
Expand Up @@ -211,6 +211,7 @@ JAVA_SOURCES = \
translate/InnerClassExtractor.java \
translate/JavaCloneWriter.java \
translate/JavaToIOSMethodTranslator.java \
translate/LabelRewriter.java \
translate/NilCheckResolver.java \
translate/OcniExtractor.java \
translate/OperatorRewriter.java \
Expand Down
Expand Up @@ -38,6 +38,7 @@
import com.google.devtools.j2objc.translate.InnerClassExtractor;
import com.google.devtools.j2objc.translate.JavaCloneWriter;
import com.google.devtools.j2objc.translate.JavaToIOSMethodTranslator;
import com.google.devtools.j2objc.translate.LabelRewriter;
import com.google.devtools.j2objc.translate.NilCheckResolver;
import com.google.devtools.j2objc.translate.OcniExtractor;
import com.google.devtools.j2objc.translate.OperatorRewriter;
Expand Down Expand Up @@ -178,9 +179,15 @@ public static void applyMutations(
}

// Adds nil_chk calls wherever an expression is dereferenced.
// Before: LabelRewriter - Control flow analysis requires original Java
// labels.
new NilCheckResolver().run(unit);
ticker.tick("NilCheckResolver");

// Rewrites labeled break and continue statements.
new LabelRewriter().run(unit);
ticker.tick("LabelRewriter");

// Before: ArrayRewriter - Adds ArrayCreation nodes.
// Before: Functionizer - Can't rewrite function arguments.
new VarargsRewriter().run(unit);
Expand Down Expand Up @@ -282,6 +289,7 @@ public static void generateObjectiveCSource(GenerationUnit unit) {
ticker.printResults(System.out);
}

@Override
protected void handleError(ProcessingContext input) {
// Causes the generation unit to release any trees it was holding.
input.getGenerationUnit().failed();
Expand Down
Expand Up @@ -19,12 +19,14 @@
import com.google.devtools.j2objc.ast.Expression;
import com.google.devtools.j2objc.ast.FieldAccess;
import com.google.devtools.j2objc.ast.InfixExpression;
import com.google.devtools.j2objc.ast.LabeledStatement;
import com.google.devtools.j2objc.ast.MethodInvocation;
import com.google.devtools.j2objc.ast.PostfixExpression;
import com.google.devtools.j2objc.ast.PrefixExpression;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.Statement;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TreeVisitor;
import com.google.devtools.j2objc.ast.VariableDeclarationStatement;
import com.google.devtools.j2objc.ast.WhileStatement;
Expand Down Expand Up @@ -61,10 +63,9 @@ public void endVisit(EnhancedForStatement node) {
}

if (expressionType.isArray()) {
node.replaceWith(makeArrayIterationBlock(
expression, expressionType, loopVariable, node.getBody()));
handleArrayIteration(node);
} else if (emitJavaIteratorLoop(loopVariable)) {
node.replaceWith(makeIterableBlock(expression, expressionType, loopVariable, node.getBody()));
convertToJavaIteratorLoop(node);
} else if (loopVariable.getType().isPrimitive()) {
boxLoopVariable(node, expressionType, loopVariable);
} else {
Expand All @@ -74,9 +75,10 @@ public void endVisit(EnhancedForStatement node) {
}
}

private Block makeArrayIterationBlock(
Expression expression, ITypeBinding expressionType, IVariableBinding loopVariable,
Statement loopBody) {
private void handleArrayIteration(EnhancedForStatement node) {
Expression expression = node.getExpression();
ITypeBinding expressionType = expression.getTypeBinding();
IVariableBinding loopVariable = node.getParameter().getVariableBinding();
ITypeBinding componentType = expressionType.getComponentType();
ITypeBinding iosArrayType = typeEnv.resolveArrayType(componentType);
PointerTypeBinding bufferType = typeEnv.getPointerType(componentType);
Expand All @@ -94,7 +96,7 @@ private Block makeArrayIterationBlock(
"size", Modifier.PUBLIC, typeEnv.resolveJavaType("int"), true, false, iosArrayType, null);

VariableDeclarationStatement arrayDecl =
new VariableDeclarationStatement(arrayVariable, expression.copy());
new VariableDeclarationStatement(arrayVariable, TreeUtil.remove(expression));
FieldAccess bufferAccess = new FieldAccess(bufferField, new SimpleName(arrayVariable));
VariableDeclarationStatement bufferDecl =
new VariableDeclarationStatement(bufferVariable, bufferAccess);
Expand All @@ -107,7 +109,7 @@ bufferType, InfixExpression.Operator.PLUS, new SimpleName(bufferVariable),
loop.setExpression(new InfixExpression(
typeEnv.resolveJavaType("boolean"), InfixExpression.Operator.LESS,
new SimpleName(bufferVariable), new SimpleName(endVariable)));
Block newLoopBody = makeBlock(loopBody.copy());
Block newLoopBody = makeBlock(TreeUtil.remove(node.getBody()));
loop.setBody(newLoopBody);
newLoopBody.getStatements().add(0, new VariableDeclarationStatement(
loopVariable, new PrefixExpression(
Expand All @@ -120,8 +122,7 @@ componentType, PrefixExpression.Operator.DEREFERENCE, new PostfixExpression(
stmts.add(bufferDecl);
stmts.add(endDecl);
stmts.add(loop);

return block;
replaceLoop(node, block, loop);
}

private boolean emitJavaIteratorLoop(IVariableBinding loopVariable) {
Expand All @@ -138,9 +139,10 @@ private boolean emitJavaIteratorLoop(IVariableBinding loopVariable) {
return false;
}

private Block makeIterableBlock(
Expression expression, ITypeBinding expressionType, IVariableBinding loopVariable,
Statement loopBody) {
private void convertToJavaIteratorLoop(EnhancedForStatement node) {
Expression expression = node.getExpression();
ITypeBinding expressionType = expression.getTypeBinding();
IVariableBinding loopVariable = node.getParameter().getVariableBinding();
ITypeBinding iterableType = BindingUtil.findInterface(expressionType, "java.lang.Iterable");
IMethodBinding iteratorMethod = BindingUtil.findDeclaredMethod(iterableType, "iterator");
ITypeBinding iteratorType = iteratorMethod.getReturnType();
Expand All @@ -151,15 +153,16 @@ private Block makeIterableBlock(
IVariableBinding iteratorVariable = new GeneratedVariableBinding(
"iter__", 0, iteratorType, false, false, null, null);

MethodInvocation iteratorInvocation = new MethodInvocation(iteratorMethod, expression.copy());
MethodInvocation iteratorInvocation =
new MethodInvocation(iteratorMethod, TreeUtil.remove(expression));
VariableDeclarationStatement iteratorDecl =
new VariableDeclarationStatement(iteratorVariable, iteratorInvocation);
MethodInvocation hasNextInvocation =
new MethodInvocation(hasNextMethod, new SimpleName(iteratorVariable));
MethodInvocation nextInvocation =
new MethodInvocation(nextMethod, new SimpleName(iteratorVariable));

Block newLoopBody = makeBlock(loopBody.copy());
Block newLoopBody = makeBlock(TreeUtil.remove(node.getBody()));
newLoopBody.getStatements().add(
0, new VariableDeclarationStatement(loopVariable, nextInvocation));

Expand All @@ -171,8 +174,18 @@ private Block makeIterableBlock(
List<Statement> stmts = block.getStatements();
stmts.add(iteratorDecl);
stmts.add(whileLoop);
replaceLoop(node, block, whileLoop);
}

return block;
private void replaceLoop(EnhancedForStatement oldLoop, Statement replacement, Statement newLoop) {
if (oldLoop.getParent() instanceof LabeledStatement) {
LabeledStatement labeledStmt = (LabeledStatement) oldLoop.getParent();
labeledStmt.replaceWith(replacement);
newLoop.replaceWith(labeledStmt);
labeledStmt.setBody(newLoop);
} else {
oldLoop.replaceWith(replacement);
}
}

private void boxLoopVariable(
Expand Down
@@ -0,0 +1,141 @@
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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.devtools.j2objc.translate;

import com.google.devtools.j2objc.ast.Block;
import com.google.devtools.j2objc.ast.BreakStatement;
import com.google.devtools.j2objc.ast.ContinueStatement;
import com.google.devtools.j2objc.ast.DoStatement;
import com.google.devtools.j2objc.ast.EmptyStatement;
import com.google.devtools.j2objc.ast.EnhancedForStatement;
import com.google.devtools.j2objc.ast.ForStatement;
import com.google.devtools.j2objc.ast.LabeledStatement;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.Statement;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TreeVisitor;
import com.google.devtools.j2objc.ast.WhileStatement;

import java.util.HashMap;
import java.util.Map;

/**
* Rewrites multiple labels that have the same name.
* Rewrites labeled break and continue statements, converting them to goto for
* the correct control flow.
*
* @author Tom Ball, Keith Stanger
*/
public class LabelRewriter extends TreeVisitor {

@Override
public boolean visit(MethodDeclaration node) {
// Rename any labels that have the same names; legal in Java but not C.
final Map<String, Integer> labelCounts = new HashMap<>();
node.accept(new TreeVisitor() {
@Override
public void endVisit(LabeledStatement labeledStatement) {
final String name = labeledStatement.getLabel().getIdentifier();
int value = labelCounts.containsKey(name) ? labelCounts.get(name) + 1 : 1;
labelCounts.put(name, value);
if (value > 1) {
final String newName = name + '_' + value;
labeledStatement.setLabel(new SimpleName(newName));
// Update references to this label.
labeledStatement.accept(new TreeVisitor() {
@Override
public void endVisit(ContinueStatement node) {
if (node.getLabel() != null && node.getLabel().getIdentifier().equals(name)) {
node.setLabel(new SimpleName(newName));
}
}
@Override
public void endVisit(BreakStatement node) {
if (node.getLabel() != null && node.getLabel().getIdentifier().equals(name)) {
node.setLabel(new SimpleName(newName));
}
}
});

}
}
});
return true;
}

private static Statement getLoopBody(Statement s) {
if (s instanceof DoStatement) {
return ((DoStatement) s).getBody();
} else if (s instanceof EnhancedForStatement) {
return ((EnhancedForStatement) s).getBody();
} else if (s instanceof ForStatement) {
return ((ForStatement) s).getBody();
} else if (s instanceof WhileStatement) {
return ((WhileStatement) s).getBody();
}
return null;
}

@Override
public void endVisit(LabeledStatement node) {
Statement loopBody = getLoopBody(node.getBody());

final String labelIdentifier = node.getLabel().getIdentifier();

final boolean[] hasContinue = new boolean[1];
final boolean[] hasBreak = new boolean[1];
node.accept(new TreeVisitor() {
@Override
public void endVisit(ContinueStatement node) {
if (node.getLabel() != null && node.getLabel().getIdentifier().equals(labelIdentifier)) {
hasContinue[0] = true;
node.setLabel(new SimpleName("continue_" + labelIdentifier));
}
}
@Override
public void endVisit(BreakStatement node) {
if (node.getLabel() != null && node.getLabel().getIdentifier().equals(labelIdentifier)) {
hasBreak[0] = true;
node.setLabel(new SimpleName("break_" + labelIdentifier));
}
}
});

if (hasContinue[0]) {
assert loopBody != null : "Continue statements must be inside a loop.";
LabeledStatement newLabelStmt = new LabeledStatement("continue_" + labelIdentifier);
newLabelStmt.setBody(new EmptyStatement());
// Put the loop body into an inner block so the continue label is outside
// the scope of any variable initializations.
Block newBlock = new Block();
loopBody.replaceWith(newBlock);
newBlock.getStatements().add(loopBody);
newBlock.getStatements().add(newLabelStmt);
}
if (hasBreak[0]) {
LabeledStatement newLabelStmt = new LabeledStatement("break_" + labelIdentifier);
newLabelStmt.setBody(new EmptyStatement());
TreeUtil.insertAfter(node, newLabelStmt);
}

if (hasContinue[0] || hasBreak[0]) {
// Replace this node with its statement, thus deleting the label.
node.replaceWith(TreeUtil.remove(node.getBody()));
}
}
}

0 comments on commit 5b19426

Please sign in to comment.