mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Speed up TeaVM compiler
This commit is contained in:
parent
fe2adc4675
commit
824cc79901
|
@ -600,7 +600,6 @@ public class ProgramIO {
|
|||
try {
|
||||
output.writeByte(39);
|
||||
output.writeShort(insn.getObjectRef().getIndex());
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IOExceptionWrapper(e);
|
||||
}
|
||||
|
@ -611,7 +610,6 @@ public class ProgramIO {
|
|||
try {
|
||||
output.writeByte(40);
|
||||
output.writeShort(insn.getObjectRef().getIndex());
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IOExceptionWrapper(e);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,14 @@ public final class GraphUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static int[][] findStronglyConnectedComponents(Graph graph, int[] start) {
|
||||
return findStronglyConnectedComponents(graph, start, new GraphNodeFilter() {
|
||||
@Override public boolean match(int node) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Tarjan's algorithm
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.teavm.dependency;
|
||||
|
||||
import com.carrotsearch.hppc.IntOpenHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.teavm.common.*;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class DataFlowGraphBuilder implements InstructionReader {
|
||||
private int lastIndex;
|
||||
private GraphBuilder builder = new GraphBuilder();
|
||||
private IntSet importantNodes = new IntOpenHashSet();
|
||||
private ObjectIntMap<MethodReference> methodNodes = new ObjectIntOpenHashMap<>();
|
||||
private ObjectIntMap<FieldReference> fieldNodes = new ObjectIntOpenHashMap<>();
|
||||
private int[] arrayNodes;
|
||||
|
||||
public void important(int node) {
|
||||
importantNodes.add(node);
|
||||
}
|
||||
|
||||
public int[] buildMapping(ProgramReader program, int paramCount) {
|
||||
lastIndex = program.variableCount();
|
||||
arrayNodes = new int[lastIndex];
|
||||
Arrays.fill(arrayNodes, -1);
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlockReader block = program.basicBlockAt(i);
|
||||
for (PhiReader phi : block.readPhis()) {
|
||||
for (IncomingReader incoming : phi.readIncomings()) {
|
||||
builder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
|
||||
}
|
||||
}
|
||||
block.readAllInstructions(this);
|
||||
}
|
||||
Graph graph = builder.build();
|
||||
|
||||
DisjointSet classes = new DisjointSet();
|
||||
for (int i = 0; i < lastIndex; ++i) {
|
||||
classes.create();
|
||||
}
|
||||
IntegerArray startNodes = new IntegerArray(graph.size());
|
||||
for (int i = paramCount; i < graph.size(); ++i) {
|
||||
if (!importantNodes.contains(i) && graph.incomingEdgesCount(i) == 1) {
|
||||
classes.union(graph.incomingEdges(i)[0], i);
|
||||
}
|
||||
if (graph.incomingEdgesCount(i) == 0) {
|
||||
startNodes.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph, startNodes.getAll());
|
||||
for (int[] scc : sccs) {
|
||||
int first = scc[0];
|
||||
for (int i = 1; i < scc.length; ++i) {
|
||||
classes.union(first, scc[i]);
|
||||
}
|
||||
}
|
||||
return classes.pack(program.variableCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void location(InstructionLocation location) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classConstant(VariableReader receiver, ValueType cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullConstant(VariableReader receiver) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void integerConstant(VariableReader receiver, int cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void longConstant(VariableReader receiver, long cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floatConstant(VariableReader receiver, float cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doubleConstant(VariableReader receiver, double cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stringConstant(VariableReader receiver, String cst) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
|
||||
NumericOperandType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assign(VariableReader receiver, VariableReader assignee) {
|
||||
builder.addEdge(assignee.getIndex(), receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
|
||||
builder.addEdge(value.getIndex(), receiver.getIndex());
|
||||
important(receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
|
||||
NumericOperandType targetType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
|
||||
CastIntegerDirection targetType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
|
||||
BasicBlockReader alternative) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
|
||||
BasicBlockReader consequent, BasicBlockReader alternative) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jump(BasicBlockReader target) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
|
||||
BasicBlockReader defaultTarget) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(VariableReader valueToReturn) {
|
||||
if (valueToReturn != null) {
|
||||
important(valueToReturn.getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void raise(VariableReader exception) {
|
||||
important(exception.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType, List<? extends VariableReader> dimensions) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(VariableReader receiver, String type) {
|
||||
}
|
||||
|
||||
private int getFieldNode(FieldReference field) {
|
||||
int fieldNode = fieldNodes.getOrDefault(field, -1);
|
||||
if (fieldNode < 0) {
|
||||
fieldNode = lastIndex++;
|
||||
fieldNodes.put(field, fieldNode);
|
||||
}
|
||||
important(fieldNode);
|
||||
return fieldNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getField(VariableReader receiver, VariableReader instance, FieldReference field, ValueType fieldType) {
|
||||
int fieldNode = getFieldNode(field);
|
||||
builder.addEdge(fieldNode, receiver.getIndex());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void putField(VariableReader instance, FieldReference field, VariableReader value) {
|
||||
int fieldNode = getFieldNode(field);
|
||||
builder.addEdge(value.getIndex(), fieldNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void arrayLength(VariableReader receiver, VariableReader array) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneArray(VariableReader receiver, VariableReader array) {
|
||||
important(receiver.getIndex());
|
||||
builder.addEdge(array.getIndex(), receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
|
||||
if (elementType == ArrayElementType.OBJECT) {
|
||||
builder.addEdge(array.getIndex(), receiver.getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
private int getArrayElementNode(int array) {
|
||||
int node = arrayNodes[array];
|
||||
if (node < 0) {
|
||||
node = lastIndex++;
|
||||
arrayNodes[array] = node;
|
||||
}
|
||||
important(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
builder.addEdge(getArrayElementNode(array.getIndex()), receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
builder.addEdge(value.getIndex(), getArrayElementNode(array.getIndex()));
|
||||
}
|
||||
|
||||
private int getMethodNode(MethodReference method) {
|
||||
int methodNode = methodNodes.getOrDefault(method, -1);
|
||||
if (methodNode < 0) {
|
||||
methodNode = lastIndex++;
|
||||
methodNodes.put(method, methodNode);
|
||||
}
|
||||
important(methodNode);
|
||||
return methodNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
if (receiver != null) {
|
||||
builder.addEdge(getMethodNode(method), receiver.getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initClass(String className) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullCheck(VariableReader receiver, VariableReader value) {
|
||||
builder.addEdge(value.getIndex(), receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitorEnter(VariableReader objectRef) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitorExit(VariableReader objectRef) {
|
||||
}
|
||||
}
|
|
@ -15,13 +15,7 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.callgraph.DefaultCallGraph;
|
||||
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||
|
@ -202,6 +196,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
});
|
||||
}
|
||||
|
||||
private Set<String> classesAddedByRoot = new HashSet<>();
|
||||
|
||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
||||
ClassDependency dep = classCache.map(className);
|
||||
boolean added = true;
|
||||
|
@ -210,6 +206,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
if (!addClassAccess(callGraphNode, className, callLocation.getSourceLocation())) {
|
||||
added = false;
|
||||
}
|
||||
} else {
|
||||
added = classesAddedByRoot.add(className);
|
||||
}
|
||||
if (!dep.isMissing() && added) {
|
||||
for (DependencyListener listener : listeners) {
|
||||
|
@ -249,6 +247,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
return dependency;
|
||||
}
|
||||
|
||||
private Set<MethodReference> methodsAddedByRoot = new HashSet<>();
|
||||
|
||||
public MethodDependency linkMethod(MethodReference methodRef, CallLocation callLocation) {
|
||||
if (methodRef == null) {
|
||||
throw new IllegalArgumentException();
|
||||
|
@ -262,6 +262,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
if (callLocation != null && callLocation.getMethod() != null) {
|
||||
added = callGraph.getNode(callLocation.getMethod()).addCallSite(methodRef,
|
||||
callLocation.getSourceLocation());
|
||||
} else {
|
||||
added = methodsAddedByRoot.add(methodRef);
|
||||
}
|
||||
MethodDependency graph = methodCache.map(methodRef);
|
||||
if (!graph.isMissing() && added) {
|
||||
|
@ -393,10 +395,14 @@ public class DependencyChecker implements DependencyInfo {
|
|||
return classCache.getCachedPreimages();
|
||||
}
|
||||
|
||||
private Set<FieldReference> fieldsAddedByRoot = new HashSet<>();
|
||||
|
||||
public FieldDependency linkField(final FieldReference fieldRef, final CallLocation location) {
|
||||
boolean added = true;
|
||||
if (location != null) {
|
||||
added = callGraph.getNode(location.getMethod()).addFieldAccess(fieldRef, location.getSourceLocation());
|
||||
} else {
|
||||
added = fieldsAddedByRoot.add(fieldRef);
|
||||
}
|
||||
FieldDependency dep = fieldCache.map(fieldRef);
|
||||
if (!dep.isMissing()) {
|
||||
|
|
|
@ -52,7 +52,15 @@ class DependencyGraphBuilder {
|
|||
System.out.println(new ListingBuilder().buildListing(program, " "));
|
||||
}
|
||||
resultNode = dep.getResult();
|
||||
nodes = dep.getVariables();
|
||||
|
||||
DependencyNode[] origNodes = dep.getVariables();
|
||||
int[] nodeMapping = new DataFlowGraphBuilder().buildMapping(program, dep.getParameterCount());
|
||||
nodes = new DependencyNode[origNodes.length];
|
||||
for (int i = 0; i < nodes.length; ++i) {
|
||||
nodes[i] = origNodes[nodeMapping[i]];
|
||||
}
|
||||
dep.setVariables(nodes);
|
||||
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlockReader block = program.basicBlockAt(i);
|
||||
currentExceptionConsumer = createExceptionConsumer(dep, block);
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.*;
|
|||
public class DependencyNode implements ValueDependencyInfo {
|
||||
private DependencyChecker dependencyChecker;
|
||||
private Set<DependencyConsumer> followers = new HashSet<>();
|
||||
private BitSet types = new BitSet();
|
||||
private BitSet types;
|
||||
private Map<DependencyNode, DependencyNodeToNodeTransition> transitions = new HashMap<>();
|
||||
private volatile String tag;
|
||||
private DependencyNode arrayItemNode;
|
||||
|
@ -46,6 +46,9 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
if (degree > 2) {
|
||||
return;
|
||||
}
|
||||
if (types == null) {
|
||||
types = new BitSet();
|
||||
}
|
||||
if (!types.get(type.index)) {
|
||||
types.set(type.index);
|
||||
if (DependencyChecker.shouldLog) {
|
||||
|
@ -65,10 +68,13 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
if (type.getDependencyChecker() != dependencyChecker) {
|
||||
throw new IllegalArgumentException("The given type does not belong to the same dependency checker");
|
||||
}
|
||||
if (!this.types.get(type.index)) {
|
||||
if (this.types == null || !this.types.get(type.index)) {
|
||||
types[j++] = type;
|
||||
}
|
||||
}
|
||||
if (this.types == null) {
|
||||
this.types = new BitSet();
|
||||
}
|
||||
for (int i = 0; i < j; ++i) {
|
||||
this.types.set(types[i].index);
|
||||
if (DependencyChecker.shouldLog) {
|
||||
|
@ -81,7 +87,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
|
||||
public void addConsumer(DependencyConsumer consumer) {
|
||||
if (followers.add(consumer)) {
|
||||
if (followers.add(consumer) && this.types != null) {
|
||||
List<DependencyType> types = new ArrayList<>();
|
||||
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
|
||||
types.add(dependencyChecker.types.get(index));
|
||||
|
@ -91,6 +97,9 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
}
|
||||
|
||||
public void connect(DependencyNode node, DependencyTypeFilter filter) {
|
||||
if (this == node) {
|
||||
return;
|
||||
}
|
||||
DependencyNodeToNodeTransition transition = new DependencyNodeToNodeTransition(this, node, filter);
|
||||
if (!transitions.containsKey(node)) {
|
||||
transitions.put(node, transition);
|
||||
|
@ -123,11 +132,11 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
|
||||
@Override
|
||||
public boolean hasArrayType() {
|
||||
return arrayItemNode != null && arrayItemNode.types.isEmpty();
|
||||
return arrayItemNode != null && arrayItemNode.types != null && !arrayItemNode.types.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasType(DependencyType type) {
|
||||
return type.getDependencyChecker() == dependencyChecker && types.get(type.index);
|
||||
return types != null && type.getDependencyChecker() == dependencyChecker && types.get(type.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -137,6 +146,9 @@ public class DependencyNode implements ValueDependencyInfo {
|
|||
|
||||
@Override
|
||||
public String[] getTypes() {
|
||||
if (types == null) {
|
||||
return new String[0];
|
||||
}
|
||||
List<String> result = new ArrayList<>();
|
||||
for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) {
|
||||
result.add(dependencyChecker.types.get(index).getName());
|
||||
|
|
|
@ -55,6 +55,10 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
return Arrays.copyOf(variableNodes, variableNodes.length);
|
||||
}
|
||||
|
||||
void setVariables(DependencyNode[] variables) {
|
||||
this.variableNodes = variables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVariableCount() {
|
||||
return variableNodes.length;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Specifies a fully qualified name of a method, including its name, class name, parameter types
|
||||
* and return value type. This class overloads <code>equals</code> and <code>hashCode</code>
|
||||
|
@ -28,11 +30,16 @@ package org.teavm.model;
|
|||
*/
|
||||
public class MethodReference {
|
||||
private String className;
|
||||
private MethodDescriptor descriptor;
|
||||
private String name;
|
||||
private ValueType[] signature;
|
||||
private transient MethodDescriptor descriptor;
|
||||
private transient String reprCache;
|
||||
|
||||
public MethodReference(String className, MethodDescriptor descriptor) {
|
||||
this.className = className;
|
||||
this.descriptor = descriptor;
|
||||
this.name = descriptor.getName();
|
||||
this.signature = descriptor.getSignature();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,11 +59,21 @@ public class MethodReference {
|
|||
* a type of a returning value, and all the remaining elements are types of arguments.
|
||||
*/
|
||||
public MethodReference(String className, String name, ValueType... signature) {
|
||||
this(className, new MethodDescriptor(name, signature));
|
||||
this.className = className;
|
||||
this.name = name;
|
||||
this.signature = Arrays.copyOf(signature, signature.length);
|
||||
}
|
||||
|
||||
public MethodReference(Class<?> cls, String name, Class<?>... signature) {
|
||||
this(cls.getName(), new MethodDescriptor(name, signature));
|
||||
this(cls.getName(), name, convertSignature(signature));
|
||||
}
|
||||
|
||||
private static ValueType[] convertSignature(Class<?>... signature) {
|
||||
ValueType[] types = new ValueType[signature.length];
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
types[i] = ValueType.parse(signature[i]);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
|
@ -64,44 +81,61 @@ public class MethodReference {
|
|||
}
|
||||
|
||||
public MethodDescriptor getDescriptor() {
|
||||
if (descriptor == null) {
|
||||
descriptor = new MethodDescriptor(name, signature);
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public int parameterCount() {
|
||||
return descriptor.parameterCount();
|
||||
return signature.length - 1;
|
||||
}
|
||||
|
||||
public ValueType[] getParameterTypes() {
|
||||
return descriptor.getParameterTypes();
|
||||
return Arrays.copyOf(signature, signature.length - 1);
|
||||
}
|
||||
|
||||
public ValueType[] getSignature() {
|
||||
return descriptor.getSignature();
|
||||
return Arrays.copyOf(signature, signature.length);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return descriptor.getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return className.hashCode() ^ descriptor.hashCode();
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MethodReference)) {
|
||||
return false;
|
||||
}
|
||||
MethodReference other = (MethodReference)obj;
|
||||
return className.equals(other.className) && descriptor.equals(other.descriptor);
|
||||
return toString().equals(other.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return className + "." + descriptor;
|
||||
if (reprCache == null) {
|
||||
reprCache = className + "." + name + signatureToString();
|
||||
}
|
||||
return reprCache;
|
||||
}
|
||||
|
||||
public String signatureToString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
for (int i = 0; i < signature.length - 1; ++i) {
|
||||
sb.append(signature[i].toString());
|
||||
}
|
||||
sb.append(')');
|
||||
sb.append(signature[signature.length - 1]);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user