Skip to content

Commit

Permalink
Moves try-with-resources rewriting into Rewriter.java.
Browse files Browse the repository at this point in the history
	Change on 2017/05/30 by kstanger <kstanger@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=157484905
  • Loading branch information
kstanger committed May 31, 2017
1 parent 7696341 commit 71ecf6b
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 213 deletions.
Expand Up @@ -292,19 +292,6 @@ public boolean visit(CastExpression node) {
return false; return false;
} }


private void printMultiCatch(CatchClause node) {
SingleVariableDeclaration exception = node.getException();
for (Type exceptionType : ((UnionType) exception.getType()).getTypes()) {
buffer.append("@catch (");
exceptionType.accept(this);
buffer.append(" *");
buffer.append(nameTable.getVariableQualifiedName(exception.getVariableElement()));
buffer.append(") {\n");
printStatements(node.getBody().getStatements());
buffer.append("}\n");
}
}

@Override @Override
public boolean visit(CharacterLiteral node) { public boolean visit(CharacterLiteral node) {
buffer.append(UnicodeUtils.escapeCharLiteral(node.charValue())); buffer.append(UnicodeUtils.escapeCharLiteral(node.charValue()));
Expand Down Expand Up @@ -837,24 +824,10 @@ public boolean visit(ThrowStatement node) {


@Override @Override
public boolean visit(TryStatement node) { public boolean visit(TryStatement node) {
List<VariableDeclarationExpression> resources = node.getResources(); assert node.getResources().isEmpty() : "try-with-resources is handled by Rewriter.";
boolean hasResources = !resources.isEmpty();
boolean extendedTryWithResources = hasResources
&& (!node.getCatchClauses().isEmpty() || node.getFinally() != null);

if (hasResources && !extendedTryWithResources) {
printBasicTryWithResources(node.getBody(), resources);
return false;
}


buffer.append("@try "); buffer.append("@try ");
if (extendedTryWithResources) { node.getBody().accept(this);
// Put resources inside the body of this statement (JSL 14.20.3.2).
printBasicTryWithResources(node.getBody(), resources);
} else {
node.getBody().accept(this);
}
buffer.append(' ');


for (CatchClause cc : node.getCatchClauses()) { for (CatchClause cc : node.getCatchClauses()) {
if (cc.getException().getType() instanceof UnionType) { if (cc.getException().getType() instanceof UnionType) {
Expand All @@ -876,54 +849,17 @@ public boolean visit(TryStatement node) {
return false; return false;
} }


/** private void printMultiCatch(CatchClause node) {
* Print basic try-with-resources, as defined by JLS 14.20.3.1. SingleVariableDeclaration exception = node.getException();
*/ for (Type exceptionType : ((UnionType) exception.getType()).getTypes()) {
private void printBasicTryWithResources(Block body, buffer.append("@catch (");
List<VariableDeclarationExpression> resources) { exceptionType.accept(this);
VariableDeclarationExpression resource = resources.get(0); buffer.append(" *");
// Resource declaration can only have one fragment. buffer.append(nameTable.getVariableQualifiedName(exception.getVariableElement()));
String resourceName = buffer.append(") {\n");
nameTable.getVariableQualifiedName(resource.getFragment(0).getVariableElement()); printStatements(node.getBody().getStatements());
String primaryExceptionName = UnicodeUtils.format("__primaryException%d", resources.size()); buffer.append("}\n");

buffer.append("{\n");
resource.accept(this);
buffer.append(";\n");
buffer.append(UnicodeUtils.format("NSException *%s = nil;\n", primaryExceptionName));

buffer.append("@try ");
List<VariableDeclarationExpression> tail = resources.subList(1, resources.size());
if (tail.isEmpty()) {
body.accept(this);
} else {
printBasicTryWithResources(body, tail);
} }

buffer.append(UnicodeUtils.format(
"@catch (NSException *e) {\n"
+ "%s = e;\n"
+ "@throw e;\n"
+ "}\n", primaryExceptionName));

buffer.append(UnicodeUtils.format(
// Including !=nil in the tests isn't necessary, but makes it easier
// to compare to the JLS spec.
"@finally {\n"
+ " if (%s != nil) {\n"
+ " if (%s != nil) {\n"
+ " @try {\n"
+ " [%s close];\n"
+ " } @catch (NSException *e) {\n"
+ " [%s addSuppressedWithNSException:e];\n"
+ " }\n"
+ " } else {\n"
+ " [%s close];\n"
+ " }\n"
+ " }\n"
+ "}\n",
resourceName, primaryExceptionName, resourceName, primaryExceptionName, resourceName));
buffer.append("}\n");
} }


@Override @Override
Expand Down
Expand Up @@ -738,6 +738,7 @@ public boolean visit(TryStatement node) {
pushScope(); pushScope();
for (CatchClause catchClause : node.getCatchClauses()) { for (CatchClause catchClause : node.getCatchClauses()) {
scope.mergeDownAndReset(); scope.mergeDownAndReset();
addSafeVar(catchClause.getException().getVariableElement());
catchClause.accept(this); catchClause.accept(this);
} }
popAndMerge(); popAndMerge();
Expand Down
Expand Up @@ -20,29 +20,41 @@
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration; import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.AnnotationTypeDeclaration; import com.google.devtools.j2objc.ast.AnnotationTypeDeclaration;
import com.google.devtools.j2objc.ast.Assignment;
import com.google.devtools.j2objc.ast.Block; import com.google.devtools.j2objc.ast.Block;
import com.google.devtools.j2objc.ast.BodyDeclaration; import com.google.devtools.j2objc.ast.BodyDeclaration;
import com.google.devtools.j2objc.ast.CatchClause;
import com.google.devtools.j2objc.ast.CompilationUnit; import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.ast.EnumDeclaration; import com.google.devtools.j2objc.ast.EnumDeclaration;
import com.google.devtools.j2objc.ast.Expression; import com.google.devtools.j2objc.ast.Expression;
import com.google.devtools.j2objc.ast.ExpressionStatement;
import com.google.devtools.j2objc.ast.FieldAccess; import com.google.devtools.j2objc.ast.FieldAccess;
import com.google.devtools.j2objc.ast.FieldDeclaration; import com.google.devtools.j2objc.ast.FieldDeclaration;
import com.google.devtools.j2objc.ast.ForStatement; import com.google.devtools.j2objc.ast.ForStatement;
import com.google.devtools.j2objc.ast.IfStatement;
import com.google.devtools.j2objc.ast.InfixExpression; import com.google.devtools.j2objc.ast.InfixExpression;
import com.google.devtools.j2objc.ast.MethodDeclaration; import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.MethodInvocation;
import com.google.devtools.j2objc.ast.NullLiteral;
import com.google.devtools.j2objc.ast.PackageDeclaration; import com.google.devtools.j2objc.ast.PackageDeclaration;
import com.google.devtools.j2objc.ast.ParenthesizedExpression; import com.google.devtools.j2objc.ast.ParenthesizedExpression;
import com.google.devtools.j2objc.ast.PropertyAnnotation; import com.google.devtools.j2objc.ast.PropertyAnnotation;
import com.google.devtools.j2objc.ast.QualifiedName; import com.google.devtools.j2objc.ast.QualifiedName;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration; import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.Statement; import com.google.devtools.j2objc.ast.Statement;
import com.google.devtools.j2objc.ast.ThrowStatement;
import com.google.devtools.j2objc.ast.TreeNode;
import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TryStatement;
import com.google.devtools.j2objc.ast.Type; import com.google.devtools.j2objc.ast.Type;
import com.google.devtools.j2objc.ast.TypeDeclaration; import com.google.devtools.j2objc.ast.TypeDeclaration;
import com.google.devtools.j2objc.ast.UnitTreeVisitor; import com.google.devtools.j2objc.ast.UnitTreeVisitor;
import com.google.devtools.j2objc.ast.VariableDeclarationExpression; import com.google.devtools.j2objc.ast.VariableDeclarationExpression;
import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.ast.VariableDeclarationFragment;
import com.google.devtools.j2objc.ast.VariableDeclarationStatement; import com.google.devtools.j2objc.ast.VariableDeclarationStatement;
import com.google.devtools.j2objc.types.ExecutablePair;
import com.google.devtools.j2objc.types.GeneratedVariableElement;
import com.google.devtools.j2objc.util.ElementUtil; import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.ErrorUtil; import com.google.devtools.j2objc.util.ErrorUtil;
import com.google.devtools.j2objc.util.TypeUtil; import com.google.devtools.j2objc.util.TypeUtil;
Expand All @@ -55,6 +67,7 @@
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement; import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
Expand Down Expand Up @@ -374,4 +387,94 @@ public void endVisit(PackageDeclaration node) {
unit.setHasNullabilityAnnotations(); unit.setHasNullabilityAnnotations();
} }
} }

@Override
public boolean visit(TryStatement node) {
// This visit rewrites try-with-resources constructs into regular try statements according to
// JLS 14.20.3. The rewriting is done in a visit instead of endVisit because the mutations may
// result in more try-with-resources constructs that need to be rewritten recursively.
List<VariableDeclarationExpression> resources = node.getResources();
if (resources.isEmpty()) {
return true;
}

if (!node.getCatchClauses().isEmpty() || node.getFinally() != null) {
// Extended try-with-resources. (JLS 14.20.3.2)
// The new innerTry statement will be a "Basic try-with-resources" and will be rewritten by
// the code below when it is visited.
TryStatement innerTry = new TryStatement().setBody(TreeUtil.remove(node.getBody()));;
TreeUtil.moveList(resources, innerTry.getResources());
node.setBody(new Block().addStatement(innerTry));
return true;
}

// Basic try-with-resources. (JLS 14.20.3.1)
DeclaredType throwableType = (DeclaredType) typeUtil.getJavaThrowable().asType();
VariableElement primaryException = GeneratedVariableElement.newLocalVar(
"__primaryException" + resources.size(), throwableType, null);

List<VariableDeclarationFragment> resourceFrags = resources.remove(0).getFragments();
assert resourceFrags.size() == 1;
VariableDeclarationFragment resourceFrag = resourceFrags.get(0);
VariableElement resourceVar = resourceFrag.getVariableElement();

DeclaredType closeableType =
typeUtil.findSupertype(resourceVar.asType(), "java.lang.AutoCloseable");
ExecutablePair closeMethod = typeUtil.findMethod(closeableType, "close");
ExecutablePair addSuppressedMethod =
typeUtil.findMethod(throwableType, "addSuppressed", "java.lang.Throwable");

Block block = new Block();
block.addStatement(new VariableDeclarationStatement(
resourceVar, TreeUtil.remove(resourceFrag.getInitializer())));

block.addStatement(new VariableDeclarationStatement(
primaryException, new NullLiteral(typeUtil.getNull())));

// If the current try node is the only statement in its parent block then replace the parent
// block instead of the try node to avoid extra nesting of braces.
TreeNode parent = node.getParent();
if (parent instanceof Block && ((Block) parent).getStatements().size() == 1) {
parent.replaceWith(block);
} else {
node.replaceWith(block);
}
block.addStatement(TreeUtil.remove(node));

VariableElement caughtException =
GeneratedVariableElement.newLocalVar("e", throwableType, null);
Block catchBlock = new Block()
.addStatement(new ExpressionStatement(new Assignment(
new SimpleName(primaryException), new SimpleName(caughtException))))
.addStatement(new ThrowStatement(new SimpleName(caughtException)));
node.addCatchClause(new CatchClause()
.setException(new SingleVariableDeclaration(caughtException))
.setBody(catchBlock));

Statement closeResource = new ExpressionStatement(new MethodInvocation(
closeMethod, typeUtil.getVoid(), new SimpleName(resourceVar)));
VariableElement suppressedException =
GeneratedVariableElement.newLocalVar("e", throwableType, null);
node.setFinally(new Block().addStatement(new IfStatement()
.setExpression(new InfixExpression(
typeUtil.getBoolean(), InfixExpression.Operator.NOT_EQUALS,
new SimpleName(resourceVar), new NullLiteral(typeUtil.getNull())))
.setThenStatement(new Block().addStatement(new IfStatement()
.setExpression(new InfixExpression(
typeUtil.getBoolean(), InfixExpression.Operator.NOT_EQUALS,
new SimpleName(primaryException), new NullLiteral(typeUtil.getNull())))
.setThenStatement(new Block().addStatement(new TryStatement()
.setBody(new Block().addStatement(closeResource))
.addCatchClause(new CatchClause()
.setException(new SingleVariableDeclaration(suppressedException))
.setBody(new Block().addStatement(new ExpressionStatement(new MethodInvocation(
addSuppressedMethod, typeUtil.getVoid(), new SimpleName(primaryException))
.addArgument(new SimpleName(suppressedException))))))))
.setElseStatement(new Block().addStatement(closeResource.copy()))))));

// Visit the new block instead of the current node because some of content of the node (eg. the
// resource initializer) has been moved outside of the try node.
block.accept(this);
return false;
}
} }
Expand Up @@ -95,6 +95,7 @@ public final class TypeUtil {
private final TypeElement javaString; private final TypeElement javaString;
private final TypeElement javaClass; private final TypeElement javaClass;
private final TypeElement javaNumber; private final TypeElement javaNumber;
private final TypeElement javaThrowable;


private final Map<TypeElement, TypeElement> javaToObjcTypeMap; private final Map<TypeElement, TypeElement> javaToObjcTypeMap;


Expand All @@ -109,7 +110,7 @@ public TypeUtil(ParserEnvironment env, ElementUtil elementUtil) {
javaString = (TypeElement) env.resolve("java.lang.String"); javaString = (TypeElement) env.resolve("java.lang.String");
javaClass = (TypeElement) env.resolve("java.lang.Class"); javaClass = (TypeElement) env.resolve("java.lang.Class");
javaNumber = (TypeElement) env.resolve("java.lang.Number"); javaNumber = (TypeElement) env.resolve("java.lang.Number");
TypeElement javaThrowable = (TypeElement) env.resolve("java.lang.Throwable"); javaThrowable = (TypeElement) env.resolve("java.lang.Throwable");
TypeElement javaCloneable = (TypeElement) env.resolve("java.lang.Cloneable"); TypeElement javaCloneable = (TypeElement) env.resolve("java.lang.Cloneable");


javaToObjcTypeMap = ImmutableMap.<TypeElement, TypeElement>builder() javaToObjcTypeMap = ImmutableMap.<TypeElement, TypeElement>builder()
Expand Down Expand Up @@ -302,6 +303,10 @@ public TypeElement getJavaNumber() {
return javaNumber; return javaNumber;
} }


public TypeElement getJavaThrowable() {
return javaThrowable;
}

public boolean isString(TypeElement e) { public boolean isString(TypeElement e) {
return javaString.equals(e) || NS_STRING.equals(e); return javaString.equals(e) || NS_STRING.equals(e);
} }
Expand Down

0 comments on commit 71ecf6b

Please sign in to comment.