/*
* Copyright 2004-2010 Brian S O'Neill
*
* 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 org.cojen.classfile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.io.PrintWriter;
/**
* CodeAssembler implementation that prints out instructions using a Java-like
* syntax that matches the methods of CodeAssembler. When used in conjunction
* with a {@link CodeDisassembler}, this class makes it easier to understand
* how to use a CodeAssembler.
*
* @author Brian S O'Neill
*/
public class CodeAssemblerPrinter extends AbstractCodeAssembler implements CodeAssembler {
private final LocalVariable[] mParams;
private final boolean mIsStatic;
private final PrintWriter mWriter;
private final String mLinePrefix;
private final String mLineSuffix;
private final String mBulder;
private boolean mNeedSeparatorLine;
private int mLocalCounter;
private int mLabelCounter;
private int mTypeDescCounter;
// Maps TypeDesc objects to String variable names.
private Map<TypeDesc, String> mTypeDescNames;
private int mTypeDescArrayCounter;
// Maps TypeDesc arrays to String variable names.
private Map<List<TypeDesc>, String> mTypeDescArrayNames;
public CodeAssemblerPrinter(TypeDesc[] paramTypes, boolean isStatic,
PrintWriter writer)
{
this(paramTypes, isStatic, writer, null, null, null);
}
/**
* @param linePrefix optional prefix for each line
* @param lineSuffix optional suffix for each line
* @param builder when specified, generated method calls are invoked on
* local variable by this name
*/
public CodeAssemblerPrinter(TypeDesc[] paramTypes, boolean isStatic,
PrintWriter writer,
String linePrefix, String lineSuffix,
String builder)
{
mIsStatic = isStatic;
mWriter = writer;
mLinePrefix = linePrefix;
mLineSuffix = lineSuffix;
if (builder == null || builder.length() == 0) {
builder = "";
} else if (!builder.endsWith(".")) {
builder += '.';
}
mBulder = builder;
mTypeDescNames = new HashMap<TypeDesc, String>();
mTypeDescArrayNames = new HashMap<List<TypeDesc>, String>();
mParams = new LocalVariable[paramTypes.length];
int varNum = (isStatic) ? 0 : 1;
for (int i = 0; i<paramTypes.length; i++) {
String varName = "var_" + (++mLocalCounter);
println("LocalVariable " + varName + " = " +
mBulder + "getParameter(" + i + ')');
LocalVariable localVar = new NamedLocal(varName, paramTypes[i], varNum);
varNum += (localVar.isDoubleWord() ? 2 : 1);
mParams[i] = localVar;
}
}
public int getParameterCount() {
return mParams.length;
}
public LocalVariable getParameter(int index) {
return mParams[index];
}
public LocalVariable createLocalVariable(String name,
TypeDesc type) {
String varName = "var_" + (++mLocalCounter);
if (name != null) {
name = '"' + name + '"';
}
println("LocalVariable " + varName + " = " +
mBulder + "createLocalVariable(" + name +
", " + getTypeDescName(type) + ')');
return new NamedLocal(varName, type, -1);
}
public Label createLabel() {
String name = "label_" + (++mLabelCounter);
println("Label " + name + " = " + mBulder + "createLabel()");
return new NamedLabel(name);
}
public void exceptionHandler(Location startLocation,
Location endLocation,
String catchClassName) {
println(mBulder + "exceptionHandler(" +
getLabelName(startLocation) + ", " +
getLabelName(endLocation) + ", " +
(catchClassName == null ? "null" : ('"' + catchClassName + '"')) +
')');
}
public void mapLineNumber(int lineNumber) {
separatorLine();
println(mBulder + "mapLineNumber(" + lineNumber + ')');
}
public void loadNull() {
println(mBulder + "loadNull()");
}
public void loadConstant(String value) {
if (value == null) {
loadNull();
} else {
println(mBulder + "loadConstant(\"" + escape(value) + "\")");
}
}
public void loadConstant(TypeDesc type) {
if (type == null) {
loadNull();
} else {
println(mBulder + "loadConstant(" + getTypeDescName(type) + ')');
}
}
public void loadConstant(boolean value) {
println(mBulder + "loadConstant(" + value + ')');
}
public void loadConstant(int value) {
println(mBulder + "loadConstant(" + value + ')');
}
public void loadConstant(long value) {
println(mBulder + "loadConstant(" + value + "L)");
}
public void loadConstant(float value) {
String str;
if (value != value) {
str = "0.0f/0.0f";
} else if (value == Float.NEGATIVE_INFINITY) {
str = "-1.0f/0.0f";
} else if (value == Float.POSITIVE_INFINITY) {
str = "1.0f/0.0f";
} else {
str = String.valueOf(value) + 'f';
}
println(mBulder + "loadConstant(" + str + ")");
}
public void loadConstant(double value) {
String str;
if (value != value) {
str = "0.0d/0.0d";
} else if (value == Double.NEGATIVE_INFINITY) {
str = "-1.0d/0.0d";
} else if (value == Double.POSITIVE_INFINITY) {
str = "1.0d/0.0d";
} else {
str = String.valueOf(value) + 'd';
}
println(mBulder + "loadConstant(" + str + ")");
}
public void loadLocal(LocalVariable local) {
println(mBulder + "loadLocal(" + local.getName() + ')');
}
public void loadThis() {
println(mBulder + "loadThis()");
}
public void storeLocal(LocalVariable local) {
println(mBulder + "storeLocal(" + local.getName() + ')');
}
public void loadFromArray(TypeDesc type) {
println(mBulder + "loadFromArray(" + getTypeDescName(type) + ')');
}
public void storeToArray(TypeDesc type) {
println(mBulder + "storeToArray(" + getTypeDescName(type) + ')');
}
public void loadField(String fieldName,
TypeDesc type) {
println(mBulder + "loadField(\"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void loadField(String className,
String fieldName,
TypeDesc type) {
println(mBulder + "loadField(\"" + className + "\", \"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void loadField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
println(mBulder + "loadField(\"" + getTypeDescName(classDesc) + "\", \""
+ fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void loadStaticField(String fieldName,
TypeDesc type) {
println(mBulder + "loadStaticField(\"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void loadStaticField(String className,
String fieldName,
TypeDesc type) {
println(mBulder + "loadStaticField(\"" + className + "\", \"" +
fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void loadStaticField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
println(mBulder + "loadStaticField(\"" + getTypeDescName(classDesc) + "\", \"" +
fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeField(String fieldName,
TypeDesc type) {
println(mBulder + "storeField(\"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeField(String className,
String fieldName,
TypeDesc type) {
println(mBulder + "storeField(\"" + className + "\", \"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
println(mBulder + "storeField(\"" + getTypeDescName(classDesc) + "\", \"" +
fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeStaticField(String fieldName,
TypeDesc type) {
println(mBulder + "storeStaticField(\"" + fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeStaticField(String className,
String fieldName,
TypeDesc type) {
println(mBulder + "storeStaticField(\"" + className + "\", \"" +
fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void storeStaticField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
println(mBulder + "storeStaticField(\"" + getTypeDescName(classDesc) + "\", \"" +
fieldName + "\", " +
getTypeDescName(type) + ')');
}
public void returnVoid() {
println(mBulder + "returnVoid()");
}
public void returnValue(TypeDesc type) {
println(mBulder + "returnValue(" + getTypeDescName(type) + ')');
}
public void convert(TypeDesc fromType, TypeDesc toType) {
println(mBulder + "convert(" +
getTypeDescName(fromType) + ", " +
getTypeDescName(toType) + ')');
}
public void convert(TypeDesc fromType, TypeDesc toType, int fpConvertMode) {
switch (fpConvertMode) {
default:
convert(fromType, toType);
break;
case CONVERT_FP_BITS:
println(mBulder + "convert(" +
getTypeDescName(fromType) + ", " +
getTypeDescName(toType) + ", " +
mBulder + ".CONVERT_FP_BITS" + ')');
break;
case CONVERT_FP_RAW_BITS:
println(mBulder + "convert(" +
getTypeDescName(fromType) + ", " +
getTypeDescName(toType) + ", " +
mBulder + ".CONVERT_FP_RAW_BITS" + ')');
break;
}
}
public void invokeVirtual(String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeVirtual(\"" + methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeVirtual(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeVirtual(\"" + className + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeVirtual(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeVirtual(\"" + getTypeDescName(classDesc) + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeStatic(String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeStatic(\"" + methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeStatic(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeStatic(\"" + className + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeStatic(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeStatic(\"" + getTypeDescName(classDesc) + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeInterface(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeInterface(\"" + className + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeInterface(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeInterface(\"" + getTypeDescName(classDesc) + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokePrivate(String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokePrivate(\"" + methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeSuper(String superClassName,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeSuper(\"" + superClassName + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeSuper(TypeDesc superClassDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
println(mBulder + "invokeSuper(\"" + getTypeDescName(superClassDesc) + "\", \"" +
methodName + "\", " +
getTypeDescName(ret) + ", " +
getTypeDescArrayName(params) + ')');
}
public void invokeConstructor(TypeDesc[] params) {
println(mBulder + "invokeConstructor(" +
getTypeDescArrayName(params) + ')');
}
public void invokeConstructor(String className, TypeDesc[] params) {
println(mBulder + "invokeConstructor(\"" + className + "\", " +
getTypeDescArrayName(params) + ')');
}
public void invokeConstructor(TypeDesc classDesc, TypeDesc[] params) {
println(mBulder + "invokeConstructor(\"" + getTypeDescName(classDesc) + "\", " +
getTypeDescArrayName(params) + ')');
}
public void invokeSuperConstructor(TypeDesc[] params) {
println(mBulder + "invokeSuperConstructor(" +
getTypeDescArrayName(params) + ')');
}
public void newObject(TypeDesc type) {
println(mBulder + "newObject(" + getTypeDescName(type) + ')');
}
public void newObject(TypeDesc type, int dimensions) {
if (dimensions == 0 && !type.isArray()) {
newObject(type);
} else {
println(mBulder + "newObject(" + getTypeDescName(type) + ", " +
dimensions + ')');
}
}
public void dup() {
println(mBulder + "dup()");
}
public void dupX1() {
println(mBulder + "dupX1()");
}
public void dupX2() {
println(mBulder + "dupX2()");
}
public void dup2() {
println(mBulder + "dup2()");
}
public void dup2X1() {
println(mBulder + "dup2X1()");
}
public void dup2X2() {
println(mBulder + "dup2X2()");
}
public void pop() {
println(mBulder + "pop()");
}
public void pop2() {
println(mBulder + "pop2()");
}
public void swap() {
println(mBulder + "swap()");
}
public void swap2() {
println(mBulder + "swap2()");
}
public void branch(Location location) {
println(mBulder + "branch(" + getLabelName(location) + ')');
}
public void ifNullBranch(Location location, boolean choice) {
println(mBulder + "ifNullBranch(" +
getLabelName(location) + ", " + choice + ')');
}
public void ifEqualBranch(Location location, boolean choice) {
println(mBulder + "ifEqualBranch(" +
getLabelName(location) + ", " + choice + ')');
}
public void ifZeroComparisonBranch(Location location, String choice) {
println(mBulder + "ifZeroComparisonBranch(" +
getLabelName(location) + ", \"" + choice + "\")");
}
public void ifComparisonBranch(Location location, String choice) {
println(mBulder + "ifComparisonBranch(" +
getLabelName(location) + ", \"" + choice + "\")");
}
public void switchBranch(int[] cases,
Location[] locations, Location defaultLocation) {
StringBuffer buf = new StringBuffer(cases.length * 15);
buf.append(mBulder + "switchBranch(");
buf.append("new int[] {");
for (int i=0; i<cases.length; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(cases[i]);
}
buf.append("}");
buf.append(", ");
buf.append("new Location[] {");
for (int i=0; i<locations.length; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(getLabelName(locations[i]));
}
buf.append("}");
buf.append(", ");
buf.append(getLabelName(defaultLocation));
buf.append(')');
println(buf.toString());
}
public void jsr(Location location) {
println(mBulder + "jsr(" + getLabelName(location) + ')');
}
public void ret(LocalVariable local) {
println(mBulder + "ret(" + local.getName() + ')');
}
public void math(byte opcode) {
println
(mBulder + "math(Opcode." + Opcode.getMnemonic(opcode).toUpperCase() + ')');
}
public void arrayLength() {
println(mBulder + "arrayLength()");
}
public void throwObject() {
println(mBulder + "throwObject()");
}
public void checkCast(TypeDesc type) {
println(mBulder + "checkCast(" + getTypeDescName(type) + ')');
}
public void instanceOf(TypeDesc type) {
println(mBulder + "instanceOf(" + getTypeDescName(type) + ')');
}
public void integerIncrement(LocalVariable local, int amount) {
println(mBulder + "integerIncrement(" + local.getName() + ", " + amount + ')');
}
public void monitorEnter() {
println(mBulder + "monitorEnter()");
}
public void monitorExit() {
println(mBulder + "monitorExit()");
}
public void nop() {
println(mBulder + "nop()");
}
public void breakpoint() {
println(mBulder + "breakpoint()");
}
private void separatorLine() {
if (mNeedSeparatorLine) {
mWriter.println();
mNeedSeparatorLine = false;
}
}
private void println(String str) {
mNeedSeparatorLine = true;
if (mLinePrefix != null) {
mWriter.print(mLinePrefix);
}
if (mLineSuffix == null) {
mWriter.println(str);
} else {
mWriter.print(str);
mWriter.println(mLineSuffix);
}
}
private String getLabelName(Location location) {
if (location instanceof NamedLabel) {
return ((NamedLabel)location).mName;
} else {
return ((NamedLabel)createLabel()).mName;
}
}
private String getTypeDescName(TypeDesc type) {
if (type == null) {
return "null";
}
String name = mTypeDescNames.get(type);
if (name == null) {
if (type.isPrimitive()) {
name = "TypeDesc.".concat(type.getRootName().toUpperCase());
mTypeDescNames.put(type, name);
return name;
} else if (type == TypeDesc.OBJECT) {
mTypeDescNames.put(type, name = "TypeDesc.OBJECT");
return name;
} else if (type == TypeDesc.STRING) {
mTypeDescNames.put(type, name = "TypeDesc.STRING");
return name;
}
name = "type_" + (++mTypeDescCounter);
mTypeDescNames.put(type, name);
StringBuffer buf = new StringBuffer("TypeDesc ");
buf.append(name);
buf.append(" = ");
TypeDesc componentType = type.getComponentType();
if (componentType != null) {
buf.append(getTypeDescName(componentType));
buf.append(".toArrayType(");
} else {
buf.append("TypeDesc.forClass(");
buf.append('"');
buf.append(type.getRootName());
buf.append('"');
}
buf.append(')');
println(buf.toString());
}
return name;
}
private String getTypeDescArrayName(TypeDesc[] types) {
if (types == null) {
return "null";
}
List<TypeDesc> key = Arrays.asList(types);
String name = mTypeDescArrayNames.get(key);
if (name == null) {
name = "params_" + (++mTypeDescArrayCounter);
mTypeDescArrayNames.put(key, name);
StringBuffer buf = new StringBuffer("TypeDesc[] ");
buf.append(name);
buf.append(" = new TypeDesc[] {");
for (int i=0; i<types.length; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(getTypeDescName(types[i]));
}
buf.append('}');
println(buf.toString());
}
return name;
}
static String escape(String value) {
return escape(value, false);
}
static String escape(String value, boolean forChar) {
int length = value.length();
int i = 0;
for (; i < length; i++) {
char c = value.charAt(i);
if (c < 32 || c > 126 || c == '"' || c == '\\' || (forChar && c == '\'')) {
break;
}
}
if (i >= length) {
return value;
}
StringBuffer buf = new StringBuffer(length + 16);
for (i=0; i<length; i++) {
char c = value.charAt(i);
if (c >= 32 && c <= 126 && c != '"' && c != '\\' && (!forChar || c != '\'')) {
buf.append(c);
continue;
}
switch (c) {
case '\0':
buf.append("\\0");
break;
case '"':
buf.append("\\\"");
break;
case '\'':
buf.append("\\'");
break;
case '\\':
buf.append("\\\\");
break;
case '\b':
buf.append("\\b");
break;
case '\f':
buf.append("\\f");
break;
case '\n':
buf.append("\\n");
break;
case '\r':
buf.append("\\r");
break;
case '\t':
buf.append("\\t");
break;
default:
String u = Integer.toHexString(c).toLowerCase();
buf.append("\\u");
for (int len = u.length(); len < 4; len++) {
buf.append('0');
}
buf.append(u);
break;
}
}
return buf.toString();
}
private class NamedLocal implements LocalVariable {
private final String mName;
private final TypeDesc mType;
private final int mNumber;
public NamedLocal(String name, TypeDesc type, int number) {
mName = name;
mType = type;
mNumber = number;
}
public String getName() {
return mName;
}
public void setName(String name) {
println(mName + ".setName(" + name + ')');
}
public TypeDesc getType() {
return mType;
}
public boolean isDoubleWord() {
return mType.isDoubleWord();
}
public int getNumber() {
return mNumber;
}
public Location getStartLocation() {
return null;
}
public Location getEndLocation() {
return null;
}
public Set<LocationRange> getLocationRangeSet() {
return null;
}
}
private class NamedLabel implements Label {
public final String mName;
public NamedLabel(String name) {
mName = name;
}
public Label setLocation() {
println(mName + ".setLocation()");
return this;
}
public int getLocation() {
return -1;
}
public int compareTo(Location obj) {
return 0;
}
}
}