in src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java [253:429]
public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
TextBuffer buf = new TextBuffer();
String super_qualifier = null;
boolean isInstanceThis = false;
tracer.addMapping(bytecode);
if (instance instanceof InvocationExprent) {
((InvocationExprent) instance).markUsingBoxingResult();
}
if (isStatic) {
if (isBoxingCall() && canIgnoreBoxing) {
// process general "boxing" calls, e.g. 'Object[] data = { true }' or 'Byte b = 123'
// here 'byte' and 'short' values do not need an explicit narrowing type cast
ExprProcessor.getCastedExprent(parameters.get(0), descriptor.params[0], buf, indent, false, false, false, false, tracer);
return buf;
}
ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);
if (node == null || !className.equals(node.classStruct.qualifiedName)) {
buf.append(DecompilerContext.getImportCollector().getNestedNameInClassContext(ExprProcessor.buildJavaClassName(className)));
}
}
else {
if (instance != null && instance.type == EXPRENT_VAR) {
VarExprent instVar = (VarExprent)instance;
VarVersion varPair = new VarVersion(instVar);
VarProcessor varProc = instVar.getProcessor();
if (varProc == null) {
MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
if (currentMethod != null) {
varProc = currentMethod.varproc;
}
}
String this_classname = null;
if (varProc != null) {
this_classname = varProc.getThisVars().get(varPair);
}
if (this_classname != null) {
isInstanceThis = true;
if (invocationType == INVOKE_SPECIAL) {
if (!className.equals(this_classname)) { // TODO: direct comparison to the super class?
StructClass cl = DecompilerContext.getStructContext().getClass(className);
boolean isInterface = cl != null && cl.hasModifier(CodeConstants.ACC_INTERFACE);
super_qualifier = !isInterface ? this_classname : className;
}
}
}
}
if (funcType == TYPE_GENERAL) {
if (super_qualifier != null) {
TextUtil.writeQualifiedSuper(buf, super_qualifier);
}
else if (instance != null) {
TextBuffer res = instance.toJava(indent, tracer);
if (isUnboxingCall()) {
// we don't print the unboxing call - no need to bother with the instance wrapping / casting
buf.append(res);
return buf;
}
VarType rightType = instance.getExprType();
VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, className);
if (!leftType.equals(rightType) &&
(rightType.equals(VarType.VARTYPE_OBJECT) ||
//try to preserve for navigation in certain cases: virtual call on variable
(rightType.getType() != CodeConstants.TYPE_UNKNOWN &&
instance.type == EXPRENT_VAR &&
invocationType == INVOKE_VIRTUAL &&
!leftType.equals(VarType.VARTYPE_OBJECT)))) {
buf.append("((").append(ExprProcessor.getCastTypeName(leftType, Collections.emptyList())).append(")");
if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {
res.enclose("(", ")");
}
buf.append(res).append(")");
}
else if (instance.getPrecedence() > getPrecedence()) {
buf.append("(").append(res).append(")");
}
else {
buf.append(res);
}
}
}
}
switch (funcType) {
case TYPE_GENERAL -> {
if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) {
buf = new TextBuffer();
}
if (buf.length() > 0) {
buf.append(".");
this.appendParameters(buf, genericArgs);
}
buf.append(name);
if (invocationType == INVOKE_DYNAMIC) {
buf.append("<invokedynamic>");
}
buf.append("(");
}
case TYPE_CLINIT -> throw new RuntimeException("Explicit invocation of " + CodeConstants.CLINIT_NAME);
case TYPE_INIT -> {
if (super_qualifier != null) {
buf.append("super(");
}
else if (isInstanceThis) {
buf.append("this(");
}
else if (instance != null) {
buf.append(instance.toJava(indent, tracer)).append(".<init>(");
}
else {
throw new RuntimeException("Unrecognized invocation of " + CodeConstants.INIT_NAME);
}
}
}
List<VarVersion> mask = null;
boolean isEnum = false;
if (funcType == TYPE_INIT) {
ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(className);
if (newNode != null) {
mask = ExprUtil.getSyntheticParametersMask(newNode, stringDescriptor, parameters.size());
isEnum = newNode.classStruct.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);
}
}
List<StructMethod> matches = getMatchedDescriptors();
BitSet setAmbiguousParameters = getAmbiguousParameters(matches);
// omit 'new Type[] {}' for the last parameter of a vararg method call
if (parameters.size() == descriptor.params.length && isVarArgCall()) {
Exprent lastParam = parameters.get(parameters.size() - 1);
if (lastParam.type == EXPRENT_NEW && lastParam.getExprType().getArrayDim() >= 1) {
((NewExprent) lastParam).setVarArgParam(true);
}
}
boolean firstParameter = true;
int start = isEnum ? 2 : 0;
for (int i = start; i < parameters.size(); i++) {
if (mask == null || mask.get(i) == null) {
TextBuffer buff = new TextBuffer();
boolean ambiguous = setAmbiguousParameters.get(i);
// 'byte' and 'short' literals need an explicit narrowing type cast when used as a parameter
ExprProcessor.getCastedExprent(parameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, true, true, tracer);
// the last "new Object[0]" in the vararg call is not printed
if (buff.length() > 0) {
if (!firstParameter) {
buf.append(", ");
}
buf.append(buff);
}
firstParameter = false;
}
}
buf.append(')');
return buf;
}