blob: abe8d959677ec2ae837735fa4e5a6cde645b6b46 [file] [log] [blame]
/*
* Copyright 2008 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.android.jack.ir.impl;
import com.android.jack.Jack;
import com.android.jack.ir.ast.CanBeAbstract;
import com.android.jack.ir.ast.CanBeFinal;
import com.android.jack.ir.ast.CanBeNative;
import com.android.jack.ir.ast.CanBeStatic;
import com.android.jack.ir.ast.HasName;
import com.android.jack.ir.ast.HasType;
import com.android.jack.ir.ast.JAbsentArrayDimension;
import com.android.jack.ir.ast.JAbstractMethodBody;
import com.android.jack.ir.ast.JAbstractStringLiteral;
import com.android.jack.ir.ast.JAlloc;
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JAnnotationMethod;
import com.android.jack.ir.ast.JArrayLength;
import com.android.jack.ir.ast.JArrayLiteral;
import com.android.jack.ir.ast.JArrayRef;
import com.android.jack.ir.ast.JArrayType;
import com.android.jack.ir.ast.JAssertStatement;
import com.android.jack.ir.ast.JBinaryOperation;
import com.android.jack.ir.ast.JBlock;
import com.android.jack.ir.ast.JBooleanLiteral;
import com.android.jack.ir.ast.JBreakStatement;
import com.android.jack.ir.ast.JByteLiteral;
import com.android.jack.ir.ast.JCaseStatement;
import com.android.jack.ir.ast.JCatchBlock;
import com.android.jack.ir.ast.JCharLiteral;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassLiteral;
import com.android.jack.ir.ast.JConditionalExpression;
import com.android.jack.ir.ast.JConstructor;
import com.android.jack.ir.ast.JContinueStatement;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JDoStatement;
import com.android.jack.ir.ast.JDoubleLiteral;
import com.android.jack.ir.ast.JDynamicCastOperation;
import com.android.jack.ir.ast.JEnumLiteral;
import com.android.jack.ir.ast.JExceptionRuntimeValue;
import com.android.jack.ir.ast.JExpression;
import com.android.jack.ir.ast.JExpressionStatement;
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JFieldInitializer;
import com.android.jack.ir.ast.JFieldRef;
import com.android.jack.ir.ast.JFloatLiteral;
import com.android.jack.ir.ast.JForStatement;
import com.android.jack.ir.ast.JGoto;
import com.android.jack.ir.ast.JIfStatement;
import com.android.jack.ir.ast.JInstanceOf;
import com.android.jack.ir.ast.JIntLiteral;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JLabel;
import com.android.jack.ir.ast.JLabeledStatement;
import com.android.jack.ir.ast.JLambda;
import com.android.jack.ir.ast.JLiteral;
import com.android.jack.ir.ast.JLocal;
import com.android.jack.ir.ast.JLocalRef;
import com.android.jack.ir.ast.JLock;
import com.android.jack.ir.ast.JLongLiteral;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
import com.android.jack.ir.ast.JMethodIdRef;
import com.android.jack.ir.ast.JMethodIdWide;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JMultiExpression;
import com.android.jack.ir.ast.JNameValuePair;
import com.android.jack.ir.ast.JNewArray;
import com.android.jack.ir.ast.JNewInstance;
import com.android.jack.ir.ast.JNode;
import com.android.jack.ir.ast.JNullLiteral;
import com.android.jack.ir.ast.JNullType;
import com.android.jack.ir.ast.JPackage;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JParameterRef;
import com.android.jack.ir.ast.JPhantomClassOrInterface;
import com.android.jack.ir.ast.JPostfixOperation;
import com.android.jack.ir.ast.JPrefixOperation;
import com.android.jack.ir.ast.JPrimitiveType;
import com.android.jack.ir.ast.JReferenceType;
import com.android.jack.ir.ast.JReinterpretCastOperation;
import com.android.jack.ir.ast.JReturnStatement;
import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JShortLiteral;
import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.JSynchronizedBlock;
import com.android.jack.ir.ast.JThis;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.ast.JThrowStatement;
import com.android.jack.ir.ast.JTryStatement;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JUnlock;
import com.android.jack.ir.ast.JWhileStatement;
import com.android.jack.ir.formatter.SourceFormatter;
import com.android.jack.lookup.CommonTypes;
import com.android.jack.util.TextOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Implements a reasonable textual representation for all JNodes. The goal is to have a common base
* for other specific generations (e.g. {@link JNode#toString()}, {@link JNode#toSource()}, ...).
*/
public class BaseGenerationVisitor extends TextOutputVisitor {
static final char[] CHARS_ABSTRACT = "abstract ".toCharArray();
static final char[] CHARS_ALLOC = "alloc ".toCharArray();
static final char[] CHARS_ASSERT = "assert ".toCharArray();
static final char[] CHARS_BREAK = "break".toCharArray();
static final char[] CHARS_CASE = "case ".toCharArray();
static final char[] CHARS_CATCH = " catch ".toCharArray();
static final char[] CHARS_CLASS = "class ".toCharArray();
static final char[] CHARS_COMMA = ", ".toCharArray();
static final char[] CHARS_CONTINUE = "continue".toCharArray();
static final char[] CHARS_DEFAULT = "default".toCharArray();
static final char[] CHARS_DO = "do".toCharArray();
static final char[] CHARS_DOTCLASS = ".class".toCharArray();
static final char[] CHARS_ELSE = "else".toCharArray();
static final char[] CHARS_MULTI_CATCH = "|".toCharArray();
static final char[] CHARS_EXTENDS = "extends ".toCharArray();
static final char[] CHARS_FALSE = "false".toCharArray();
static final char[] CHARS_FINAL = "final ".toCharArray();
static final char[] CHARS_FINALLY = " finally ".toCharArray();
static final char[] CHARS_FOR = "for ".toCharArray();
static final char[] CHARS_GOTO = "goto".toCharArray();
static final char[] CHARS_IF = "if ".toCharArray();
static final char[] CHARS_IMPLEMENTS = "implements ".toCharArray();
static final char[] CHARS_INSTANCEOF = " instanceof ".toCharArray();
static final char[] CHARS_INTERFACE = "interface ".toCharArray();
static final char[] CHARS_NATIVE = "native ".toCharArray();
static final char[] CHARS_NEW = "new ".toCharArray();
static final char[] CHARS_NONAME = "no-name ".toCharArray();
static final char[] CHARS_NULL = "null".toCharArray();
static final char[] CHARS_PRIVATE = "private ".toCharArray();
static final char[] CHARS_PUBLIC = "public ".toCharArray();
static final char[] CHARS_REINTERPRETCAST = "reinterpret-cast ".toCharArray();
static final char[] CHARS_RETURN = "return".toCharArray();
static final char[] CHARS_RUNTIME_EXCEPTION = "Runtime exception of type".toCharArray();
static final char[] CHARS_STATIC = "static ".toCharArray();
static final char[] CHARS_SUPER = "super".toCharArray();
static final char[] CHARS_SWITCH = "switch ".toCharArray();
static final char[] CHARS_THIS = "this".toCharArray();
static final char[] CHARS_THROW = "throw".toCharArray();
static final char[] CHARS_TRUE = "true".toCharArray();
static final char[] CHARS_TRY = "try ".toCharArray();
static final char[] CHARS_WHILE = "while ".toCharArray();
static final char[] SYNCHRONIZED_BLOCK = "synchronized ".toCharArray();
static final char[] LOCK = "lock ".toCharArray();
static final char[] UNLOCK = "unlock ".toCharArray();
static final SourceFormatter formatter = SourceFormatter.getFormatter();
protected boolean needSemi = true;
protected boolean suppressType = false;
public BaseGenerationVisitor(TextOutput textOutput) {
super(textOutput);
}
@Override
public boolean visit(@Nonnull JAbsentArrayDimension x) {
// nothing to print, parent prints []
return false;
}
@Override
public boolean visit(@Nonnull JAnnotation annotation) {
print("@");
printTypeName(annotation.getType());
lparen();
List<JNameValuePair> nameValuePairs =
new ArrayList<JNameValuePair>(annotation.getNameValuePairs());
Collections.sort(nameValuePairs, new Comparator<JNameValuePair>() {
@Override
public int compare(JNameValuePair nameValuePair1, JNameValuePair nameValuePair2) {
return nameValuePair1.getName().compareTo(nameValuePair2.getName());
}
});
visitCollectionWithCommas(nameValuePairs.iterator());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JArrayLength x) {
JExpression instance = x.getInstance();
parenPush(x, instance);
accept(instance);
parenPop(x, instance);
print(".length");
return false;
}
@Override
public boolean visit(@Nonnull JArrayLiteral arrayLiteral) {
List<JLiteral> values = arrayLiteral.getValues();
if (values.size() > 1) {
print('{');
}
visitCollectionWithCommas(values.iterator());
if (values.size() > 1) {
print('}');
}
return false;
}
@Override
public boolean visit(@Nonnull JAlloc x) {
print(CHARS_ALLOC);
print("<");
print(x.getType().getName());
print(">");
return false;
}
@Override
public boolean visit(@Nonnull JArrayRef x) {
JExpression instance = x.getInstance();
parenPush(x, instance);
accept(instance);
parenPop(x, instance);
print('[');
accept(x.getIndexExpr());
print(']');
return false;
}
@Override
public boolean visit(@Nonnull JArrayType x) {
accept(x.getElementType());
print("[]");
return false;
}
@Override
public boolean visit(@Nonnull JAssertStatement x) {
print(CHARS_ASSERT);
accept(x.getTestExpr());
JExpression arg = x.getArg();
if (arg != null) {
print(" : ");
accept(arg);
}
return false;
}
@Override
public boolean visit(@Nonnull JBinaryOperation x) {
// TODO(later): associativity
JExpression arg1 = x.getLhs();
parenPush(x, arg1);
accept(arg1);
parenPop(x, arg1);
space();
print(x.getOp().toString());
space();
JExpression arg2 = x.getRhs();
parenPush(x, arg2);
accept(arg2);
parenPop(x, arg2);
return false;
}
@Override
public boolean visit(@Nonnull JExceptionRuntimeValue x) {
print(CHARS_RUNTIME_EXCEPTION);
space();
printTypeName(x.getType());
return super.visit(x);
}
@Override
public boolean visit(@Nonnull JCatchBlock x) {
JLocal catchVar = x.getCatchVar();
if (catchVar.getType().isSameType(Jack.getSession().getPhantomLookup()
.getClass(CommonTypes.JAVA_LANG_OBJECT))) {
print(CHARS_FINALLY);
} else {
print(CHARS_CATCH);
lparen();
boolean first = true;
for (JClass catchedType : x.getCatchTypes()) {
if (first) {
first = false;
} else {
space();
print(CHARS_MULTI_CATCH);
space();
}
printTypeName(catchedType);
}
space();
printName(catchVar);
rparen();
}
space();
openBlock();
for (JStatement statement : x.getStatements()) {
needSemi = true;
accept(statement);
if (needSemi) {
semi();
}
newline();
}
closeBlock();
needSemi = false;
return false;
}
@Override
public boolean visit(@Nonnull JBlock x) {
openBlock();
for (JStatement statement : x.getStatements()) {
needSemi = true;
accept(statement);
if (needSemi) {
semi();
}
newline();
}
closeBlock();
needSemi = false;
return false;
}
@Override
public boolean visit(@Nonnull JBooleanLiteral x) {
printBooleanLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JBreakStatement x) {
print(CHARS_BREAK);
if (x.getLabel() != null) {
space();
accept(x.getLabel());
}
return false;
}
@Override
public boolean visit(@Nonnull JByteLiteral x) {
print(Byte.toString(x.getValue()));
return false;
}
@Override
public boolean visit(@Nonnull JCaseStatement x) {
JLiteral caseExpr = x.getExpr();
if (caseExpr != null) {
print(CHARS_CASE);
accept(caseExpr);
} else {
print(CHARS_DEFAULT);
}
print(':');
space();
needSemi = false;
return false;
}
@Override
public boolean visit(@Nonnull JReinterpretCastOperation x) {
lparen();
print(CHARS_REINTERPRETCAST);
printType(x);
rparen();
space();
JExpression expr = x.getExpr();
parenPush(x, expr);
accept(expr);
parenPop(x, expr);
return false;
}
@Override
public boolean visit(@Nonnull JDynamicCastOperation x) {
lparen();
Iterator<JType> typesIt = x.getTypes().iterator();
printTypeName(typesIt.next());
while (typesIt.hasNext()) {
print(" & ");
printTypeName(typesIt.next());
}
rparen();
space();
JExpression expr = x.getExpr();
parenPush(x, expr);
accept(expr);
parenPop(x, expr);
return false;
}
@Override
public boolean visit(@Nonnull JCharLiteral x) {
printCharLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JClassLiteral x) {
printTypeName(x.getRefType());
print(CHARS_DOTCLASS);
return false;
}
@Override
public boolean visit(@Nonnull JDefinedClass x) {
printAnnotationLiterals(x.getAnnotations());
printTypeFlags(x);
print(CHARS_CLASS);
printTypeName(x);
space();
JClass superClass = x.getSuperClass();
if (superClass != null) {
print(CHARS_EXTENDS);
printTypeName(superClass);
space();
}
if (x.getImplements().size() > 0) {
print(CHARS_IMPLEMENTS);
for (int i = 0, c = x.getImplements().size(); i < c; ++i) {
if (i > 0) {
print(CHARS_COMMA);
}
printTypeName(x.getImplements().get(i));
}
space();
}
return false;
}
@Override
public boolean visit(@Nonnull JConditionalExpression x) {
// TODO(later): associativity
JExpression ifTest = x.getIfTest();
parenPush(x, ifTest);
accept(ifTest);
parenPop(x, ifTest);
print(" ? ");
JExpression thenExpr = x.getThenExpr();
parenPush(x, thenExpr);
accept(thenExpr);
parenPop(x, thenExpr);
print(" : ");
JExpression elseExpr = x.getElseExpr();
parenPush(x, elseExpr);
accept(elseExpr);
parenPop(x, elseExpr);
return false;
}
@Override
public boolean visit(@Nonnull JConstructor x) {
printAnnotationLiterals(x.getAnnotations());
// Modifiers
if (x.isPrivate()) {
print(CHARS_PRIVATE);
} else {
print(CHARS_PUBLIC);
}
printName(x);
// Parameters
printParameterList(x);
if (x.isAbstract() || !shouldPrintMethodBody()) {
semi();
newlineOpt();
} else {
JMethodBody body = x.getBody();
assert body != null;
accept(body);
}
return false;
}
@Override
public boolean visit(@Nonnull JContinueStatement x) {
print(CHARS_CONTINUE);
if (x.getLabel() != null) {
space();
accept(x.getLabel());
}
return false;
}
@Override
public boolean visit(@Nonnull JFieldInitializer x) {
if (!suppressType) {
printName(x.getFieldRef().getFieldId());
} else {
accept(x.getFieldRef());
}
JExpression initializer = x.getInitializer();
print(" = ");
accept(initializer);
return false;
}
@Override
public boolean visit(@Nonnull JDoStatement x) {
print(CHARS_DO);
if (x.getBody() != null) {
nestedStatementPush(x.getBody());
accept(x.getBody());
nestedStatementPop(x.getBody());
}
if (needSemi) {
semi();
newline();
} else {
space();
needSemi = true;
}
print(CHARS_WHILE);
lparen();
accept(x.getTestExpr());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JDoubleLiteral x) {
printDoubleLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JEnumLiteral enumLiteral) {
printTypeName(enumLiteral.getType());
print('.');
print(enumLiteral.getFieldId().getName());
return false;
}
@Override
public boolean visit(@Nonnull JExpressionStatement x) {
accept(x.getExpr());
return false;
}
@Override
public boolean visit(@Nonnull JField x) {
printAnnotationLiterals(x.getAnnotations());
print(JModifier.getStringFieldModifier(x.getModifier()));
printType(x);
space();
printName(x);
return false;
}
@Override
public boolean visit(@Nonnull JFieldRef x) {
JExpression instance = x.getInstance();
if (instance != null) {
parenPush(x, instance);
accept(instance);
parenPop(x, instance);
} else {
printTypeName(x.getReceiverType());
}
print('.');
printName(x.getFieldId());
return false;
}
@Override
public boolean visit(@Nonnull JFloatLiteral x) {
printFloatLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JForStatement x) {
print(CHARS_FOR);
lparen();
Iterator<JStatement> iter = x.getInitializers().iterator();
if (iter.hasNext()) {
JStatement stmt = iter.next();
accept(stmt);
}
suppressType = true;
while (iter.hasNext()) {
print(CHARS_COMMA);
JStatement stmt = iter.next();
accept(stmt);
}
suppressType = false;
semi();
space();
if (x.getTestExpr() != null) {
accept(x.getTestExpr());
}
semi();
space();
visitCollectionWithCommas(x.getIncrements().iterator());
rparen();
if (x.getBody() != null) {
nestedStatementPush(x.getBody());
accept(x.getBody());
nestedStatementPop(x.getBody());
}
return false;
}
@Override
public boolean visit(@Nonnull JGoto x) {
print(CHARS_GOTO);
space();
accept(x.getTargetBlock().getLabel());
return false;
}
@Override
public boolean visit(@Nonnull JIfStatement x) {
print(CHARS_IF);
lparen();
accept(x.getIfExpr());
rparen();
if (x.getThenStmt() != null) {
nestedStatementPush(x.getThenStmt());
accept(x.getThenStmt());
nestedStatementPop(x.getThenStmt());
}
if (x.getElseStmt() != null) {
if (needSemi) {
semi();
newline();
} else {
space();
needSemi = true;
}
print(CHARS_ELSE);
boolean elseIf = x.getElseStmt() instanceof JIfStatement;
if (!elseIf) {
nestedStatementPush(x.getElseStmt());
} else {
space();
}
accept(x.getElseStmt());
if (!elseIf) {
nestedStatementPop(x.getElseStmt());
}
}
return false;
}
@Override
public boolean visit(@Nonnull JInstanceOf x) {
JExpression expr = x.getExpr();
parenPush(x, expr);
accept(expr);
parenPop(x, expr);
print(CHARS_INSTANCEOF);
printTypeName(x.getTestType());
return false;
}
@Override
public boolean visit(@Nonnull JDefinedInterface x) {
printAnnotationLiterals(x.getAnnotations());
printTypeFlags(x);
print(CHARS_INTERFACE);
printTypeName(x);
space();
if (x.getImplements().size() > 0) {
print(CHARS_EXTENDS);
for (int i = 0, c = x.getImplements().size(); i < c; ++i) {
if (i > 0) {
print(CHARS_COMMA);
}
printTypeName(x.getImplements().get(i));
}
space();
}
return false;
}
@Override
public boolean visit(@Nonnull JIntLiteral x) {
print(Integer.toString(x.getValue()));
return false;
}
@Override
public boolean visit(@Nonnull JLabel x) {
printName(x);
return false;
}
@Override
public boolean visit(@Nonnull JLabeledStatement x) {
accept(x.getLabel());
print(" : ");
accept(x.getBody());
return false;
}
@Override
public boolean visit(@Nonnull JLambda x) {
visitCollectionJType(
x.getMethodIdWithoutErasure().getMethodIdWide().getParamTypes().iterator());
print(" -> ");
openBlock();
print("SAM type: ");
printType(x);
newline();
print("interface bounds: ");
Iterator<JInterface> boundTypeIt = x.getInterfaceBounds().iterator();
if (boundTypeIt.hasNext()) {
printName(boundTypeIt.next());
}
while (boundTypeIt.hasNext()) {
print(CHARS_COMMA);
printName(boundTypeIt.next());
}
newline();
print("captured variables: ");
visitCollectionWithCommas(x.getCapturedVariables().iterator());
newline();
print("method to implement: ");
printJMethodId(x.getMethodIdWithErasure());
newline();
print("method to enforce: ");
printJMethodId(x.getMethodIdWithoutErasure());
newline();
print("lambda method: ");
accept(x.getMethodIdRef());
newline();
print("bridges: ");
visitCollectionJMethodId(x.getBridgeMethodIds().iterator());
newline();
closeBlock();
return false;
}
protected void visitCollectionJMethodId(@Nonnull Iterator<JMethodId> iter) {
if (iter.hasNext()) {
printJMethodId(iter.next());
}
while (iter.hasNext()) {
print(CHARS_COMMA);
printJMethodId(iter.next());
}
}
private void printJMethodId(@Nonnull JMethodId methodId) {
printType(methodId);
space();
printName(methodId.getMethodIdWide());
visitCollectionJType(methodId.getMethodIdWide().getParamTypes().iterator());
}
private void visitCollectionJType(@Nonnull Iterator<JType> iter) {
lparen();
if (iter.hasNext()) {
printName(iter.next());
}
while (iter.hasNext()) {
print(CHARS_COMMA);
printName(iter.next());
}
rparen();
}
@Override
public boolean visit(@Nonnull JLocal x) {
printAnnotationLiterals(x.getAnnotations());
printFinalFlag(x);
printType(x);
space();
printName(x);
return false;
}
@Override
public boolean visit(@Nonnull JLocalRef x) {
printName(x.getLocal());
return false;
}
@Override
public boolean visit(@Nonnull JLongLiteral x) {
printLongLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JMethod x) {
printMethodHeader(x);
if (x instanceof JAnnotationMethod) {
JLiteral defaultValue = ((JAnnotationMethod) x).getDefaultValue();
if (defaultValue != null) {
space();
print(CHARS_DEFAULT);
space();
accept(defaultValue);
semi();
newlineOpt();
}
} else if (x.isAbstract() || !shouldPrintMethodBody()) {
semi();
newlineOpt();
} else {
space();
JAbstractMethodBody body = x.getBody();
assert body != null;
accept(body);
}
return false;
}
@Override
public boolean visit(@Nonnull JMethodBody x) {
accept(x.getBlock());
return false;
}
@Override
public boolean visit(@Nonnull JMethodIdRef x) {
printTypeName(x.getEnclosingType());
print('.');
printName(x.getMethodId().getMethodIdWide());
visitCollectionJType(x.getMethodId().getMethodIdWide().getParamTypes().iterator());
return false;
}
@Override
public boolean visit(@Nonnull JMethodCall x) {
JExpression instance = x.getInstance();
JMethodIdWide target = x.getMethodId();
if (instance == null) {
// Static call.
printTypeName(x.getReceiverType());
print('.');
printName(target);
} else if (x.getInstance() instanceof JThisRef) {
// super() or this() call.
JReferenceType thisType = (JReferenceType) instance.getType();
if (thisType.isSameType(x.getReceiverType())) {
print(CHARS_THIS);
} else {
print(CHARS_SUPER);
}
if (!(x instanceof JNewInstance)) {
print('.');
printName(target);
}
} else {
// Instance call.
parenPush(x, instance);
accept(instance);
parenPop(x, instance);
print('.');
printName(target);
}
lparen();
visitCollectionWithCommas(x.getArgs().iterator());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JMultiExpression x) {
lparen();
visitCollectionWithCommas(x.exprs.iterator());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JNameValuePair nameValuePair) {
print(nameValuePair.getName());
print(" = ");
accept(nameValuePair.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JNewArray x) {
print(CHARS_NEW);
List<JExpression> initializers = x.getInitializers();
boolean hasInitializer = initializers.isEmpty();
printTypeName(!hasInitializer ? x.getArrayType() : x.getArrayType().getLeafType());
if (!hasInitializer) {
print("{");
visitCollectionWithCommas(initializers.iterator());
print('}');
} else {
List<JExpression> dims = x.getDims();
for (int i = 0; i < dims.size(); ++i) {
JExpression expr = dims.get(i);
print('[');
accept(expr);
print(']');
}
}
return false;
}
@Override
public boolean visit(@Nonnull JNewInstance x) {
print(CHARS_NEW);
JMethodIdWide target = x.getMethodId();
printName(target);
lparen();
visitCollectionWithCommas(x.getArgs().iterator());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JNullLiteral x) {
print(CHARS_NULL);
return false;
}
@Override
public boolean visit(@Nonnull JNullType x) {
printTypeName(x);
return false;
}
@Override
public boolean visit(@Nonnull JPackage pack) {
print(formatter.getName(pack));
return false;
}
@Override
public boolean visit(@Nonnull JParameter x) {
printAnnotationLiterals(x.getAnnotations());
print(JModifier.getStringVariableModifier(x.getModifier()));
printType(x);
space();
printName(x);
return false;
}
@Override
public boolean visit(@Nonnull JParameterRef x) {
printName(x.getTarget());
return false;
}
@Override
public boolean visit(@Nonnull JPhantomClassOrInterface x) {
printTypeName(x);
return false;
}
@Override
public boolean visit(@Nonnull JPostfixOperation x) {
// TODO(later): associativity
JExpression arg = x.getArg();
parenPush(x, arg);
accept(arg);
parenPop(x, arg);
print(x.getOp().toString());
return false;
}
@Override
public boolean visit(@Nonnull JPrefixOperation x) {
// TODO(later): associativity
print(x.getOp().toString());
JExpression arg = x.getArg();
parenPush(x, arg);
accept(arg);
parenPop(x, arg);
return false;
}
@Override
public boolean visit(@Nonnull JPrimitiveType x) {
printTypeName(x);
return false;
}
@Override
public boolean visit(@Nonnull JSession x) {
print("<JSession>");
return false;
}
@Override
public boolean visit(@Nonnull JReturnStatement x) {
print(CHARS_RETURN);
JExpression expr = x.getExpr();
if (expr != null) {
space();
accept(expr);
}
return false;
}
@Override
public boolean visit(@Nonnull JShortLiteral x) {
print(Short.toString(x.getValue()));
return false;
}
@Override
public boolean visit(@Nonnull JAbstractStringLiteral x) {
printStringLiteral(x.getValue());
return false;
}
@Override
public boolean visit(@Nonnull JSwitchStatement x) {
print(CHARS_SWITCH);
lparen();
accept(x.getExpr());
rparen();
space();
nestedStatementPush(x.getBody());
accept(x.getBody());
nestedStatementPop(x.getBody());
return false;
}
@Override
public boolean visit(@Nonnull JThis x) {
printType(x);
space();
printName(x);
return false;
}
@Override
public boolean visit(@Nonnull JThisRef x) {
printName(x.getTarget());
return false;
}
@Override
public boolean visit(@Nonnull JThrowStatement x) {
print(CHARS_THROW);
if (x.getExpr() != null) {
space();
accept(x.getExpr());
}
return false;
}
@Override
public boolean visit(@Nonnull JTryStatement x) {
print(CHARS_TRY);
accept(x.getTryBlock());
for (JCatchBlock catchBlock : x.getCatchBlocks()) {
accept(catchBlock);
}
JBlock finallyBlock = x.getFinallyBlock();
if (finallyBlock != null) {
print(CHARS_FINALLY);
accept(finallyBlock);
}
return false;
}
@Override
public boolean visit(@Nonnull JWhileStatement x) {
print(CHARS_WHILE);
lparen();
accept(x.getTestExpr());
rparen();
if (x.getBody() != null) {
nestedStatementPush(x.getBody());
accept(x.getBody());
nestedStatementPop(x.getBody());
}
return false;
}
@Override
public boolean visit(@Nonnull JLock x) {
print(LOCK);
lparen();
accept(x.getLockExpr());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JUnlock x) {
print(UNLOCK);
lparen();
accept(x.getLockExpr());
rparen();
return false;
}
@Override
public boolean visit(@Nonnull JSynchronizedBlock x) {
print(SYNCHRONIZED_BLOCK);
lparen();
accept(x.getLockExpr());
rparen();
space();
accept(x.getSynchronizedBlock());
return false;
}
protected void closeBlock() {
indentOut();
print('}');
}
protected void lparen() {
print('(');
}
protected void nestedStatementPop(JStatement statement) {
if (!(statement instanceof JBlock)) {
indentOut();
}
}
protected void nestedStatementPush(JStatement statement) {
if (!(statement instanceof JBlock)) {
indentIn();
newline();
} else {
space();
}
}
protected void openBlock() {
print('{');
indentIn();
newline();
}
protected boolean parenPop(int parentPrec, JExpression child) {
int childPrec = JavaPrecedenceVisitor.exec(child);
if (parentPrec < childPrec) {
rparen();
return true;
} else {
return false;
}
}
protected boolean parenPop(JExpression parent, JExpression child) {
return parenPop(JavaPrecedenceVisitor.exec(parent), child);
}
protected boolean parenPush(int parentPrec, JExpression child) {
int childPrec = JavaPrecedenceVisitor.exec(child);
if (parentPrec < childPrec) {
lparen();
return true;
} else {
return false;
}
}
protected boolean parenPush(JExpression parent, JExpression child) {
return parenPush(JavaPrecedenceVisitor.exec(parent), child);
}
protected void printTypeFlags(JDefinedClassOrInterface declaredType) {
int modifier = declaredType.getModifier();
String modifierStr = JModifier.getStringTypeModifier(modifier);
print(modifierStr);
}
protected void printAbstractFlag(CanBeAbstract x) {
if (x.isAbstract()) {
print(CHARS_ABSTRACT);
}
}
protected void printBooleanLiteral(boolean value) {
print(value ? CHARS_TRUE : CHARS_FALSE);
}
protected void printChar(char c) {
switch (c) {
case '\b':
print("\\b");
break;
case '\t':
print("\\t");
break;
case '\n':
print("\\n");
break;
case '\f':
print("\\f");
break;
case '\r':
print("\\r");
break;
case '\"':
print("\\\"");
break;
case '\'':
print("\\'");
break;
case '\\':
print("\\\\");
break;
default:
if (Character.isISOControl(c)) {
print("\\u");
if (c < 0x1000) {
print('0');
}
if (c < 0x100) {
print('0');
}
if (c < 0x10) {
print('0');
}
print(Integer.toHexString(c));
} else {
print(c);
}
}
}
protected void printCharLiteral(char value) {
print('\'');
printChar(value);
print('\'');
}
protected void printDoubleLiteral(double value) {
print(Double.toString(value));
}
protected void printFinalFlag(CanBeFinal x) {
if (x.isFinal()) {
print(CHARS_FINAL);
}
}
protected void printFloatLiteral(float value) {
print(Float.toString(value));
print('f');
}
protected void printLongLiteral(long value) {
print(Long.toString(value));
print('L');
}
protected void printMethodHeader(JMethod x) {
printAnnotationLiterals(x.getAnnotations());
// Modifiers
print(JModifier.getStringMethodModifier(x.getModifier()));
printType(x);
space();
printName(x);
// Parameters
printParameterList(x);
}
private void printAnnotationLiterals(Collection<JAnnotation> annotation) {
List<JAnnotation> annotations = new ArrayList<JAnnotation>(annotation);
Collections.sort(annotations, new Comparator<JAnnotation>() {
@Override
public int compare(JAnnotation annotation1, JAnnotation annotation2) {
return (Jack.getLookupFormatter().getName(annotation1.getType()).compareTo(
Jack.getLookupFormatter().getName(annotation2.getType())));
}
});
for (JAnnotation annotationLiteral : annotations) {
accept(annotationLiteral);
space();
}
}
protected void printName(HasName x) {
String name = x.getName();
if (name == null) {
print(CHARS_NONAME);
} else {
print(name.replace('/', '.'));
}
}
protected void printNativeFlag(CanBeNative x) {
if (x.isNative()) {
print(CHARS_NATIVE);
}
}
protected void printParameterList(JMethod x) {
lparen();
visitCollectionWithCommas(x.getParams().iterator());
rparen();
}
protected void printStaticFlag(CanBeStatic x) {
if (x.isStatic()) {
print(CHARS_STATIC);
}
}
protected void printStringLiteral(String string) {
char[] s = string.toCharArray();
print('\"');
for (int i = 0; i < s.length; ++i) {
printChar(s[i]);
}
print('\"');
}
protected void printType(HasType hasType) {
printTypeName(hasType.getType());
}
protected void printTypeName(JType type) {
print(formatter.getName(type));
}
protected void rparen() {
print(')');
}
protected void semi() {
print(';');
}
protected boolean shouldPrintMethodBody() {
return false;
}
protected void space() {
print(' ');
}
protected void visitCollectionWithCommas(Iterator<? extends JNode> iter) {
if (iter.hasNext()) {
accept(iter.next());
}
while (iter.hasNext()) {
print(CHARS_COMMA);
accept(iter.next());
}
}
}