/*
* 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.CompilationUnit;
import com.google.devtools.j2objc.ast.EnhancedForStatement;
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.UnitTreeVisitor;
import com.google.devtools.j2objc.ast.VariableDeclarationStatement;
import com.google.devtools.j2objc.ast.WhileStatement;
import com.google.devtools.j2objc.types.ExecutablePair;
import com.google.devtools.j2objc.types.GeneratedVariableElement;
import com.google.devtools.j2objc.types.PointerType;
import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.TypeUtil;
import com.google.j2objc.annotations.AutoreleasePool;
import com.google.j2objc.annotations.LoopTranslation;
import com.google.j2objc.annotations.LoopTranslation.LoopStyle;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
/**
* Rewrites Java enhanced for loops into appropriate C constructs.
*
* @author Keith Stanger
*/
public class EnhancedForRewriter extends UnitTreeVisitor {
public EnhancedForRewriter(CompilationUnit unit) {
super(unit);
}
@Override
public void endVisit(EnhancedForStatement node) {
Expression expression = node.getExpression();
TypeMirror expressionType = expression.getTypeMirror();
VariableElement loopVariable = node.getParameter().getVariableElement();
if (ElementUtil.hasAnnotation(loopVariable, AutoreleasePool.class)) {
makeBlock(node.getBody()).setHasAutoreleasePool(true);
}
if (TypeUtil.isArray(expressionType)) {
handleArrayIteration(node);
} else if (emitJavaIteratorLoop(loopVariable)) {
convertToJavaIteratorLoop(node);
} else if (loopVariable.asType().getKind().isPrimitive()) {
boxLoopVariable(node, expressionType, loopVariable);
} else {
VariableElement newLoopVariable = GeneratedVariableElement.mutableCopy(loopVariable)
.setTypeQualifiers("__strong");
node.getParameter().setVariableElement(newLoopVariable);
}
}
private void handleArrayIteration(EnhancedForStatement node) {
Expression expression = node.getExpression();
ArrayType expressionType = (ArrayType) expression.getTypeMirror();
VariableElement loopVariable = node.getParameter().getVariableElement();
TypeMirror componentType = expressionType.getComponentType();
TypeElement iosArrayType = typeUtil.getIosArray(componentType);
TypeMirror bufferType = new PointerType(componentType);
VariableElement arrayVariable = GeneratedVariableElement.newLocalVar(
"a__", expressionType, null);
VariableElement bufferVariable = GeneratedVariableElement.newLocalVar("b__", bufferType, null)
.setTypeQualifiers("const*");
VariableElement endVariable = GeneratedVariableElement.newLocalVar("e__", bufferType, null)
.setTypeQualifiers("const*");
VariableElement bufferField = GeneratedVariableElement.newField(
"buffer", bufferType, iosArrayType)
.addModifiers(Modifier.PUBLIC);
VariableElement sizeField = GeneratedVariableElement.newField(
"size", typeUtil.getInt(), iosArrayType)
.addModifiers(Modifier.PUBLIC);
VariableDeclarationStatement arrayDecl =
new VariableDeclarationStatement(arrayVariable, TreeUtil.remove(expression));
FieldAccess bufferAccess = new FieldAccess(bufferField, new SimpleName(arrayVariable));
VariableDeclarationStatement bufferDecl =
new VariableDeclarationStatement(bufferVariable, bufferAccess);
InfixExpression endInit = new InfixExpression(
bufferType, InfixExpression.Operator.PLUS, new SimpleName(bufferVariable),
new FieldAccess(sizeField, new SimpleName(arrayVariable)));
VariableDeclarationStatement endDecl = new VariableDeclarationStatement(endVariable, endInit);
WhileStatement loop = new WhileStatement();
loop.setExpression(new InfixExpression(
typeUtil.getBoolean(), InfixExpression.Operator.LESS, new SimpleName(bufferVariable),
new SimpleName(endVariable)));
Block newLoopBody = makeBlock(TreeUtil.remove(node.getBody()));
loop.setBody(newLoopBody);
newLoopBody.addStatement(0, new VariableDeclarationStatement(
loopVariable, new PrefixExpression(
componentType, PrefixExpression.Operator.DEREFERENCE,
new PostfixExpression(bufferVariable, PostfixExpression.Operator.INCREMENT))));
Block block = new Block();
List<Statement> stmts = block.getStatements();
stmts.add(arrayDecl);
stmts.add(bufferDecl);
stmts.add(endDecl);
stmts.add(loop);
replaceLoop(node, block, loop);
}
private boolean emitJavaIteratorLoop(VariableElement loopVariable) {
AnnotationMirror loopTranslation =
ElementUtil.getAnnotation(loopVariable, LoopTranslation.class);
if (loopTranslation == null) {
return false;
}
Object style = ElementUtil.getAnnotationValue(loopTranslation, "value");
if (style instanceof VariableElement
&& ElementUtil.getName((VariableElement) style).equals(LoopStyle.JAVA_ITERATOR.name())) {
return true;
}
return false;
}
private void convertToJavaIteratorLoop(EnhancedForStatement node) {
Expression expression = node.getExpression();
TypeMirror expressionType = expression.getTypeMirror();
VariableElement loopVariable = node.getParameter().getVariableElement();
DeclaredType iterableType = typeUtil.findSupertype(expressionType, "java.lang.Iterable");
ExecutablePair iteratorMethod = typeUtil.findMethod(iterableType, "iterator");
DeclaredType iteratorType = (DeclaredType) iteratorMethod.type().getReturnType();
ExecutablePair hasNextMethod = typeUtil.findMethod(iteratorType, "hasNext");
ExecutablePair nextMethod = typeUtil.findMethod(iteratorType, "next");
assert hasNextMethod != null && nextMethod != null;
VariableElement iteratorVariable = GeneratedVariableElement.newLocalVar(
"iter__", iteratorType, null);
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(TreeUtil.remove(node.getBody()));
newLoopBody.addStatement(
0, new VariableDeclarationStatement(loopVariable, nextInvocation));
WhileStatement whileLoop = new WhileStatement();
whileLoop.setExpression(hasNextInvocation);
whileLoop.setBody(newLoopBody);
Block block = new Block();
List<Statement> stmts = block.getStatements();
stmts.add(iteratorDecl);
stmts.add(whileLoop);
replaceLoop(node, block, whileLoop);
}
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(
EnhancedForStatement node, TypeMirror expressionType, VariableElement loopVariable) {
DeclaredType iterableType = typeUtil.findSupertype(expressionType, "java.lang.Iterable");
List<? extends TypeMirror> typeArgs = iterableType.getTypeArguments();
assert typeArgs.size() == 1 && typeUtil.isBoxedType(typeArgs.get(0));
VariableElement boxVariable = GeneratedVariableElement.newLocalVar(
"boxed__", typeArgs.get(0), null);
node.setParameter(new SingleVariableDeclaration(boxVariable));
makeBlock(node.getBody()).addStatement(
0, new VariableDeclarationStatement(loopVariable, new SimpleName(boxVariable)));
}
private Block makeBlock(Statement stmt) {
if (stmt instanceof Block) {
return (Block) stmt;
}
Block block = new Block();
if (stmt.getParent() != null) {
stmt.replaceWith(block);
}
block.addStatement(stmt);
return block;
}
}