/*
* Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javap;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.lang.classfile.ClassFile;
import java.lang.classfile.Opcode;
import java.lang.classfile.constantpool.*;
import java.lang.classfile.Instruction;
import java.lang.classfile.MethodModel;
import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.instruction.*;
/*
* Write the contents of a Code attribute.
*
*
This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.
*/
public class CodeWriter extends BasicWriter {
public static CodeWriter instance(Context context) {
CodeWriter instance = context.get(CodeWriter.class);
if (instance == null)
instance = new CodeWriter(context);
return instance;
}
protected CodeWriter(Context context) {
super(context);
context.put(CodeWriter.class, this);
attrWriter = AttributeWriter.instance(context);
classWriter = ClassWriter.instance(context);
constantWriter = ConstantWriter.instance(context);
sourceWriter = SourceWriter.instance(context);
tryBlockWriter = TryBlockWriter.instance(context);
stackMapWriter = StackMapWriter.instance(context);
localVariableTableWriter = LocalVariableTableWriter.instance(context);
localVariableTypeTableWriter = LocalVariableTypeTableWriter.instance(context);
typeAnnotationWriter = TypeAnnotationWriter.instance(context);
options = Options.instance(context);
}
void write(CodeAttribute attr) {
println("Code:");
indent(+1);
writeVerboseHeader(attr);
writeInstrs(attr);
writeExceptionTable(attr);
attrWriter.write(attr.attributes(), attr);
indent(-1);
}
public void writeVerboseHeader(CodeAttribute attr) {
MethodModel method = attr.parent().get();
int n = method.methodTypeSymbol().parameterCount();
if ((method.flags().flagsMask() & ClassFile.ACC_STATIC) == 0)
++n; // for 'this'
println("stack=" + attr.maxStack() +
", locals=" + attr.maxLocals() +
", args_size=" + Integer.toString(n));
}
public void writeInstrs(CodeAttribute attr) {
List detailWriters = getDetailWriters(attr);
int pc = 0;
try {
for (var coe: attr) {
if (coe instanceof Instruction instr) {
for (InstructionDetailWriter w: detailWriters)
w.writeDetails(pc, instr);
writeInstr(pc, instr, attr);
pc += instr.sizeInBytes();
}
}
} catch (IllegalArgumentException e) {
report("error at or after byte " + pc);
}
for (InstructionDetailWriter w: detailWriters)
w.flush(pc);
}
public void writeInstr(int pc, Instruction ins, CodeAttribute lr) {
print(String.format("%4d: %-13s ", pc, ins.opcode().name().toLowerCase(Locale.US)));
try {
// compute the number of indentations for the body of multi-line instructions
// This is 6 (the width of "%4d: "), divided by the width of each indentation level,
// and rounded up to the next integer.
int indentWidth = options.indentWidth;
int indent = (6 + indentWidth - 1) / indentWidth;
switch (ins) {
case BranchInstruction instr ->
print(lr.labelToBci(instr.target()));
case ConstantInstruction.ArgumentConstantInstruction instr ->
print(instr.constantValue());
case ConstantInstruction.LoadConstantInstruction instr ->
printConstantPoolRef(instr.constantEntry());
case FieldInstruction instr ->
printConstantPoolRef(instr.field());
case InvokeDynamicInstruction instr ->
printConstantPoolRefAndValue(instr.invokedynamic(), 0);
case InvokeInstruction instr -> {
if (instr.opcode() == Opcode.INVOKEINTERFACE)
printConstantPoolRefAndValue(instr.method(), instr.count());
else printConstantPoolRef(instr.method());
}
case LoadInstruction instr ->
print(instr.sizeInBytes() > 1 ? instr.slot() : "");
case StoreInstruction instr ->
print(instr.sizeInBytes() > 1 ? instr.slot() : "");
case IncrementInstruction instr ->
print(instr.slot() + ", " + instr.constant());
case LookupSwitchInstruction instr -> {
var cases = instr.cases();
print("{ // " + cases.size());
indent(indent);
for (var c : cases)
print(String.format("%n%12d: %d", c.caseValue(),
lr.labelToBci(c.target())));
print("\n default: " + lr.labelToBci(instr.defaultTarget()) + "\n}");
indent(-indent);
}
case NewMultiArrayInstruction instr ->
printConstantPoolRefAndValue(instr.arrayType(), instr.dimensions());
case NewObjectInstruction instr ->
printConstantPoolRef(instr.className());
case NewPrimitiveArrayInstruction instr ->
print(" " + instr.typeKind().upperBound().displayName());
case NewReferenceArrayInstruction instr ->
printConstantPoolRef(instr.componentType());
case TableSwitchInstruction instr -> {
print("{ // " + instr.lowValue() + " to " + instr.highValue());
indent(indent);
var caseMap = instr.cases().stream().collect(
Collectors.toMap(SwitchCase::caseValue, SwitchCase::target));
for (int i = instr.lowValue(); i <= instr.highValue(); i++)
print(String.format("%n%12d: %d", i,
lr.labelToBci(caseMap.getOrDefault(i, instr.defaultTarget()))));
print("\n default: " + lr.labelToBci(instr.defaultTarget()) + "\n}");
indent(-indent);
}
case TypeCheckInstruction instr ->
printConstantPoolRef(instr.type());
default -> {}
}
println();
} catch (IllegalArgumentException e) {
println(report(e));
}
}
private void printConstantPoolRef(PoolEntry entry) {
print("#" + entry.index());
tab();
print("// ");
constantWriter.write(entry.index());
}
private void printConstantPoolRefAndValue(PoolEntry entry, int value) {
print("#" + entry.index() + ", " + value);
tab();
print("// ");
constantWriter.write(entry.index());
}
public void writeExceptionTable(CodeAttribute attr) {
var excTable = attr.exceptionHandlers();
if (excTable.size() > 0) {
println("Exception table:");
indent(+1);
println(" from to target type");
for (var handler : excTable) {
print(String.format(" %5d %5d %5d",
attr.labelToBci(handler.tryStart()),
attr.labelToBci(handler.tryEnd()),
attr.labelToBci(handler.handler())));
print(" ");
var catch_type = handler.catchType();
if (catch_type.isEmpty()) {
println("any");
} else {
print("Class ");
println(constantWriter.stringValue(catch_type.get()));
}
}
indent(-1);
}
}
private List getDetailWriters(CodeAttribute attr) {
List detailWriters = new ArrayList<>();
if (options.details.contains(InstructionDetailWriter.Kind.SOURCE)) {
sourceWriter.reset(attr);
if (sourceWriter.hasSource())
detailWriters.add(sourceWriter);
else
println("(Source code not available)");
}
if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VARS)) {
localVariableTableWriter.reset(attr);
detailWriters.add(localVariableTableWriter);
}
if (options.details.contains(InstructionDetailWriter.Kind.LOCAL_VAR_TYPES)) {
localVariableTypeTableWriter.reset(attr);
detailWriters.add(localVariableTypeTableWriter);
}
if (options.details.contains(InstructionDetailWriter.Kind.STACKMAPS)) {
stackMapWriter.reset(attr);
stackMapWriter.writeInitialDetails();
detailWriters.add(stackMapWriter);
}
if (options.details.contains(InstructionDetailWriter.Kind.TRY_BLOCKS)) {
tryBlockWriter.reset(attr);
detailWriters.add(tryBlockWriter);
}
if (options.details.contains(InstructionDetailWriter.Kind.TYPE_ANNOS)) {
typeAnnotationWriter.reset(attr);
detailWriters.add(typeAnnotationWriter);
}
return detailWriters;
}
private AttributeWriter attrWriter;
private ClassWriter classWriter;
private ConstantWriter constantWriter;
private LocalVariableTableWriter localVariableTableWriter;
private LocalVariableTypeTableWriter localVariableTypeTableWriter;
private TypeAnnotationWriter typeAnnotationWriter;
private SourceWriter sourceWriter;
private StackMapWriter stackMapWriter;
private TryBlockWriter tryBlockWriter;
private Options options;
}