blob: d2b771585068628184286f2a27e3b3027e0189a9 [file] [log] [blame]
/*
* Copyright (c) 2013, 2018, 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.
*
* 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 vm.runtime.defmeth.shared;
import java.util.List;
import java.util.ArrayList;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import vm.runtime.defmeth.shared.data.*;
import vm.runtime.defmeth.shared.data.method.*;
import vm.runtime.defmeth.shared.data.method.body.*;
import vm.runtime.defmeth.shared.data.method.param.*;
import vm.runtime.defmeth.shared.data.method.result.*;
import static java.lang.String.*;
import nsk.share.Pair;
/**
* Construct text representation of a class.
* Used to print generated class hierarchies.
*/
public class Printer implements Visitor {
private StringBuilder sb = new StringBuilder();
private Tester t;
private String output() {
return sb.toString();
}
static private String printAcc(int acc) {
List<String> flags = new ArrayList<>();
if ((acc & ACC_STATIC) != 0) {
flags.add("static");
}
if ((acc & ACC_PUBLIC) != 0) {
flags.add("public");
}
if ((acc & ACC_PRIVATE) != 0) {
flags.add("private");
}
if ((acc & ACC_PROTECTED) != 0) {
flags.add("protected");
}
if ((acc & ACC_PUBLIC) == 0 &&
(acc & ACC_PRIVATE) == 0 &&
(acc & ACC_PROTECTED) == 0) {
flags.add("package");
}
if ((acc & ACC_STRICT) != 0) {
flags.add("strictfp");
}
if ((acc & ACC_SYNCHRONIZED) != 0) {
flags.add("synchronized");
}
return Util.intersperse(" ", flags.toArray(new String[0]));
}
static public String print(Clazz clz) {
Printer p = new Printer();
clz.visit(p);
return p.output();
}
static public String print(Method m) {
Printer p = new Printer();
m.visit(p);
return p.output();
}
@Override
public void visitClass(Clazz clz) {
throw new IllegalStateException("More specific method should be called");
}
@Override
public void visitMethod(Method m) {
sb.append(String.format(
"%s%s",
m.name(), m.desc()));
if (m.sig() != null) {
sb.append("/* <").append(m.sig()).append("> */");
}
}
@Override
public void visitConcreteClass(ConcreteClass clz) {
sb.append("class ").append(clz.name()).append(" ");
if (!clz.parent().name().equals("java.lang.Object")) {
sb.append("extends ").append(clz.parent().name()).append(" ");
}
if (clz.interfaces().length > 0) {
sb.append("implements ");
sb.append(Util.intersperse(", ", Util.asStrings(clz.interfaces())));
sb.append(" ");
}
Method[] methods = clz.methods();
sb.append("{");
if (methods.length > 0) {
for (Method m : methods) {
sb.append("\n ");
m.visit(this);
}
sb.append("\n");
}
sb.append("}");
}
@Override
public void visitInterface(Interface intf) {
sb.append("interface ").append(intf.name())
.append(" ");
if (intf.parents().length > 0) {
sb.append("extends ");
sb.append(Util.intersperse(", ", Util.asStrings(intf.parents())));
sb.append(" ");
}
Method[] methods = intf.methods();
sb.append("{");
if (methods.length > 0) {
for (Method m : methods) {
sb.append("\n ");
m.visit(this);
}
sb.append("\n");
}
sb.append("}");
}
@Override
/* ====================================================================== */
public void visitTester(Tester t) {
this.t = t;
//sb.append(t.name()).append(": ");
sb.append("TEST: ");
//t.getCall().visit(this);
CallMethod call = t.getCall();
// call.receiverClass() is null when a .staticCallSite() invoke is
// used. There is a staticClass but no receiverClass.
sb.append(format("%s o = new %s(); o.%s%s",
call.staticClass().name(),
(call.receiverClass() == null ? "" : call.receiverClass().name()),
call.methodName(),
call.methodDesc()));
sb.append(" ");
t.getResult().visit(this);
this.t = null;
}
/* ====================================================================== */
@Override
public void visitAbstractMethod(AbstractMethod m) {
Pair<String[],String> desc = Util.parseDesc(m.desc());
sb.append(format(
"abstract %s %s %s(%s);",
printAcc(m.acc()),
Util.decodeClassName(desc.second),
m.name(),
Util.intersperse(", ", desc.first)));
if (m.sig() != null) {
sb.append("<").append(m.sig()).append(">");
}
}
@Override
public void visitConcreteMethod(ConcreteMethod m) {
Pair<String[],String> desc = Util.parseDesc(m.desc());
sb.append(format(
"%s %s %s(%s)",
printAcc(m.acc()),
Util.decodeClassName(desc.second),
m.name(),
Util.intersperse(", ", desc.first)));
if (m.sig() != null) {
sb.append("<").append(m.sig()).append(">");
}
sb.append(" ");
sb.append(" { ");
m.body().visit(this);
sb.append(" }");
}
@Override
public void visitDefaultMethod(DefaultMethod m) {
Pair<String[],String> desc = Util.parseDesc(m.desc());
sb.append(format(
"default %s %s %s(%s)",
printAcc(m.acc()),
Util.decodeClassName(desc.second),
m.name(),
Util.intersperse(", ", desc.first)));
if (m.sig() != null) {
sb.append("<").append(m.sig()).append(">");
}
sb.append(" { ");
m.body().visit(this);
sb.append(" }");
}
/* ====================================================================== */
@Override
public void visitThrowExBody(ThrowExBody body) {
sb.append(String.format(
"throw new %s();", body.getExc().name()));
}
@Override
public void visitReturnIntBody(ReturnIntBody body) {
sb.append(String.format(
"return %d;", body.getValue()));
}
@Override
public void visitReturnNullBody(ReturnNullBody body) {
sb.append("return null;");
}
@Override
public void visitEmptyBody(EmptyBody aThis) {
}
/* ====================================================================== */
@Override
public void visitResultIgnore() {
sb.append("/* result ignored */");
}
@Override
public void visitResultInt(IntResult res) {
sb.append("== ").append(res.getExpected());
}
@Override
public void visitResultThrowExc(ThrowExResult res) {
sb.append(String.format(
"throws %s%s",
abbreviateExcName(res.getExc().name()),
res.getMessage() != null ? "(\"" + res.getMessage() + "\")" : ""));
}
private String abbreviateExcName(String name) {
switch(name) {
case "java.lang.AbstractMethodError" : return "AME";
case "java.lang.NoSuchMethodError" : return "NSME";
default: return name.replaceAll("java\\.lang\\.", "");
}
}
/* ====================================================================== */
@Override
public void visitParamInt(IntParam param) {
sb.append(param.value());
}
@Override
public void visitParamString(StringParam param) {
sb.append(param.value());
}
@Override
public void visitParamNull() {
sb.append("null");
}
@Override
public void visitParamLong(LongParam param) {
sb.append(param.value());
}
@Override
public void visitParamFloat(FloatParam param) {
sb.append(param.value());
}
@Override
public void visitParamDouble(DoubleParam param) {
sb.append(param.value());
}
@Override
public void visitParamNewInstance(NewInstanceParam param) {
sb.append(String.format(
"new %s()",
param.clazz().name()));
}
@Override
public void visitCallMethod(CallMethod call) {
String[] paramTypes = Util.parseDesc(call.methodDesc()).first;
if (paramTypes.length != call.params().length) {
throw new IllegalStateException();
}
//sb.append("{ ");
if (!call.popReturnValue()) {
sb.append("return ");
}
switch (call.invokeInsn()) {
case VIRTUAL: case INTERFACE:
sb.append(String.format(
"((%s)%s).%s(",
call.staticClass().name(),
call.receiverClass() != null ? call.receiverClass().name() : "this",
call.methodName()));
break;
case STATIC:
sb.append(String.format(
"%s.%s(",
call.staticClass().name(),
call.methodName()));
break;
case SPECIAL:
sb.append(String.format(
"%s.super.%s(",
call.staticClass().name(),
call.methodName()));
break;
default:
throw new IllegalStateException();
}
for (int i = 0; i<paramTypes.length; i++) {
sb.append(String.format(
"(%s)", paramTypes[i]));
call.params()[i].visit(this);
if (i+1 < paramTypes.length) {
sb.append(", ");
}
}
//sb.append("); }");
sb.append(");");
}
@Override
public void visitReturnNewInstanceBody(ReturnNewInstanceBody body) {
sb.append(String.format(
"return new %s();",
body.getType().name()));
}
}