TextBuffer (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.EXPRENT_VAR) { VarExprent instVar = (VarExprent)instance; VarVersionPair varPair = new VarVersionPair(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 (rightType.equals(VarType.VARTYPE_OBJECT) && !leftType.equals(rightType)) { 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("."); } 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<VarVersionPair> 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); } } BitSet setAmbiguousParameters = getAmbiguousParameters(); // 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; }