/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* 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.google.errorprone.util;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.code.Types.DefaultTypeVisitor;
import com.sun.tools.javac.code.Types.SignatureGenerator;
import com.sun.tools.javac.util.Name;
import java.util.Arrays;
/** Signature generation. */
public class Signatures {
/** Returns the binary names of the class. */
public static String classDescriptor(Type type, Types types) {
SigGen sig = new SigGen(types);
sig.assembleClassSig(types.erasure(type));
return sig.toString();
}
/** Returns a JVMS 4.3.3 method descriptor. */
public static String descriptor(Type type, Types types) {
SigGen sig = new SigGen(types);
sig.assembleSig(types.erasure(type));
return sig.toString();
}
private static class SigGen extends SignatureGenerator {
private final com.sun.tools.javac.util.ByteBuffer buffer =
new com.sun.tools.javac.util.ByteBuffer();
protected SigGen(Types types) {
super(types);
}
@Override
protected void append(char ch) {
buffer.appendByte(ch);
}
@Override
protected void append(byte[] ba) {
buffer.appendBytes(ba);
}
@Override
protected void append(Name name) {
buffer.appendName(name);
}
@Override
public String toString() {
// We could use buffer.toName(Names), but we want a string anyways and this
// avoids plumbing a Context or instances of Names through.
// Names always uses UTF-8 internally.
return new String(Arrays.copyOf(buffer.elems, buffer.length), UTF_8);
}
}
/**
* Pretty-prints a method signature for use in diagnostics.
*
* <p>Uses simple names for declared types, and omitting formal type parameters and the return
* type since they do not affect overload resolution.
*/
public static String prettyMethodSignature(ClassSymbol origin, MethodSymbol m) {
StringBuilder sb = new StringBuilder();
if (!m.owner.equals(origin)) {
sb.append(m.owner.getSimpleName()).append('.');
}
sb.append(m.isConstructor() ? origin.getSimpleName() : m.getSimpleName()).append('(');
sb.append(
m.getParameters()
.stream()
.map(v -> v.type.accept(PRETTY_TYPE_VISITOR, null))
.collect(joining(", ")));
sb.append(')');
return sb.toString();
}
/** Pretty-prints a Type for use in diagnostics, using simple names for class types */
public static String prettyType(Type type) {
return type.accept(PRETTY_TYPE_VISITOR, null);
}
private static final Type.Visitor<String, Void> PRETTY_TYPE_VISITOR =
new DefaultTypeVisitor<String, Void>() {
@Override
public String visitWildcardType(Type.WildcardType t, Void aVoid) {
StringBuilder sb = new StringBuilder();
sb.append(t.kind);
if (t.kind != BoundKind.UNBOUND) {
sb.append(t.type.accept(this, null));
}
return sb.toString();
}
@Override
public String visitClassType(Type.ClassType t, Void s) {
StringBuilder sb = new StringBuilder();
sb.append(t.tsym.getSimpleName());
if (t.getTypeArguments().nonEmpty()) {
sb.append('<');
sb.append(
t.getTypeArguments()
.stream()
.map(a -> a.accept(this, null))
.collect(joining(", ")));
sb.append(">");
}
return sb.toString();
}
@Override
public String visitCapturedType(Type.CapturedType t, Void s) {
return t.wildcard.accept(this, null);
}
@Override
public String visitArrayType(Type.ArrayType t, Void aVoid) {
return t.elemtype.accept(this, null) + "[]";
}
@Override
public String visitType(Type t, Void s) {
return t.toString();
}
};
}