/* * 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 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 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 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 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