public ClassReference14Processor() { InvocationExprent invfor = new InvocationExprent(); invfor.setName("forName"); invfor.setClassname("java/lang/Class"); invfor.setStringDescriptor("(Ljava/lang/String;)Ljava/lang/Class;"); invfor.setDescriptor(MethodDescriptor.parseDescriptor("(Ljava/lang/String;)Ljava/lang/Class;")); invfor.setStatic(true); invfor.setLstParameters( Arrays.asList(new Exprent[] {new VarExprent(0, VarType.VARTYPE_STRING, null)})); bodyexprent = new ExitExprent(ExitExprent.EXIT_RETURN, invfor, VarType.VARTYPE_CLASS, null); InvocationExprent constr = new InvocationExprent(); constr.setName("<init>"); constr.setClassname("java/lang/NoClassDefFoundError"); constr.setStringDescriptor("()V"); constr.setFunctype(InvocationExprent.TYP_INIT); constr.setDescriptor(MethodDescriptor.parseDescriptor("()V")); NewExprent newexpr = new NewExprent( new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/NoClassDefFoundError"), new ArrayList<Exprent>(), null); newexpr.setConstructor(constr); InvocationExprent invcause = new InvocationExprent(); invcause.setName("initCause"); invcause.setClassname("java/lang/NoClassDefFoundError"); invcause.setStringDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); invcause.setDescriptor( MethodDescriptor.parseDescriptor("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")); invcause.setInstance(newexpr); invcause.setLstParameters( Arrays.asList( new Exprent[] { new VarExprent( 2, new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/ClassNotFoundException"), null) })); handlerexprent = new ExitExprent(ExitExprent.EXIT_THROW, invcause, null, null); }
private static void appendRenameComment( TextBuffer buffer, String oldName, MType type, int indent) { if (oldName == null) return; buffer.appendIndent(indent); buffer.append("// $FF: renamed from: "); switch (type) { case CLASS: buffer.append(ExprProcessor.buildJavaClassName(oldName)); break; case FIELD: String[] fParts = oldName.split(" "); FieldDescriptor fd = FieldDescriptor.parseDescriptor(fParts[2]); buffer.append(fParts[1]); buffer.append(' '); buffer.append(getTypePrintOut(fd.type)); break; default: String[] mParts = oldName.split(" "); MethodDescriptor md = MethodDescriptor.parseDescriptor(mParts[2]); buffer.append(mParts[1]); buffer.append(" ("); boolean first = true; for (VarType paramType : md.params) { if (!first) { buffer.append(", "); } first = false; buffer.append(getTypePrintOut(paramType)); } buffer.append(") "); buffer.append(getTypePrintOut(md.ret)); } buffer.appendLineSeparator(); }
public void classLambdaToJava( ClassNode node, TextBuffer buffer, Exprent method_object, int indent, BytecodeMappingTracer origTracer) { ClassWrapper wrapper = node.getWrapper(); if (wrapper == null) { return; } boolean lambdaToAnonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS); ClassNode outerNode = (ClassNode) DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); BytecodeMappingTracer tracer = new BytecodeMappingTracer(origTracer.getCurrentSourceLine()); try { StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(node.simpleName); if (node.lambdaInformation.is_method_reference) { if (!node.lambdaInformation.is_content_method_static && method_object != null) { // reference to a virtual method buffer.append(method_object.toJava(indent, tracer)); } else { // reference to a static method buffer.append( ExprProcessor.getCastTypeName( new VarType(node.lambdaInformation.content_class_name, false))); } buffer.append("::"); buffer.append(node.lambdaInformation.content_method_name); } else { // lambda method StructMethod mt = cl.getMethod(node.lambdaInformation.content_method_key); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambdaInformation.content_method_descriptor); MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambdaInformation.method_descriptor); if (!lambdaToAnonymous) { buffer.append('('); boolean firstParameter = true; int index = node.lambdaInformation.is_content_method_static ? 0 : 1; int start_index = md_content.params.length - md_lambda.params.length; for (int i = 0; i < md_content.params.length; i++) { if (i >= start_index) { if (!firstParameter) { buffer.append(", "); } String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); buffer.append( parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; } index += md_content.params[i].stackSize; } buffer.append(") ->"); } buffer.append(" {").appendLineSeparator(); tracer.incrementCurrentSourceLine(); methodLambdaToJava(node, wrapper, mt, buffer, indent + 1, !lambdaToAnonymous, tracer); buffer.appendIndent(indent).append("}"); addTracer(cl, mt, tracer); } } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode); } DecompilerContext.getLogger().endWriteClass(); }
private boolean methodToJava( ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); boolean hideMethod = false; int start_index_method = buffer.length(); MethodWrapper outerWrapper = (MethodWrapper) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); try { boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); boolean clinit = false, init = false, dinit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); int flags = mt.getAccessFlags(); if ((flags & CodeConstants.ACC_NATIVE) != 0) { flags &= ~CodeConstants .ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp } if (CodeConstants.CLINIT_NAME.equals(mt.getName())) { flags &= CodeConstants .ACC_STATIC; // ignore all modifiers except 'static' in a static initializer } if (isDeprecated) { appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName( cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); appendRenameComment(buffer, oldName, MType.METHOD, indent); } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; if (isSynthetic) { appendComment(buffer, "synthetic method", indent); } if (isBridge) { appendComment(buffer, "bridge method", indent); } appendAnnotations(buffer, mt, indent); buffer.appendIndent(indent); appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED); if (isInterface && mt.containsCode()) { // 'default' modifier (Java 8) buffer.append("default "); } String name = mt.getName(); if (CodeConstants.INIT_NAME.equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; dinit = true; } else { name = node.simpleName; init = true; } } else if (CodeConstants.CLINIT_NAME.equals(name)) { name = ""; clinit = true; } GenericMethodDescriptor descriptor = null; if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute) mt.getAttributes().getWithKey("Signature"); if (attr != null) { descriptor = GenericMain.parseMethodSignature(attr.getSignature()); if (descriptor != null) { int actualParams = md.params.length; List<VarVersionPair> sigFields = methodWrapper.signatureFields; if (sigFields != null) { actualParams = 0; for (VarVersionPair field : methodWrapper.signatureFields) { if (field == null) { actualParams++; } } } else if (isEnum && init) actualParams -= 2; if (actualParams != descriptor.params.size()) { String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName; DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); descriptor = null; } } } } boolean throwsExceptions = false; int paramCount = 0; if (!clinit && !dinit) { boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); if (descriptor != null && !descriptor.fparameters.isEmpty()) { appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); buffer.append(' '); } if (!init) { if (descriptor != null) { buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret)); } else { buffer.append(ExprProcessor.getCastTypeName(md.ret)); } buffer.append(' '); } buffer.append(toValidJavaIdentifier(name)); buffer.append('('); // parameters List<VarVersionPair> signFields = methodWrapper.signatureFields; int lastVisibleParameterIndex = -1; for (int i = 0; i < md.params.length; i++) { if (signFields == null || signFields.get(i) == null) { lastVisibleParameterIndex = i; } } boolean firstParameter = true; int index = isEnum && init ? 3 : thisVar ? 1 : 0; boolean hasDescriptor = descriptor != null; int start = isEnum && init && !hasDescriptor ? 2 : 0; int params = hasDescriptor ? descriptor.params.size() : md.params.length; for (int i = start; i < params; i++) { if (hasDescriptor || (signFields == null || signFields.get(i) == null)) { if (!firstParameter) { buffer.append(", "); } appendParameterAnnotations(buffer, mt, paramCount); if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } if (descriptor != null) { GenericType parameterType = descriptor.params.get(i); boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); if (isVarArg) { parameterType = parameterType.decreaseArrayDim(); } String typeName = GenericMain.getGenericCastTypeName(parameterType); if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && DecompilerContext.getOption( IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } buffer.append(typeName); if (isVarArg) { buffer.append("..."); } } else { VarType parameterType = md.params[i]; boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); if (isVarArg) { parameterType = parameterType.decreaseArrayDim(); } String typeName = ExprProcessor.getCastTypeName(parameterType); if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && DecompilerContext.getOption( IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } buffer.append(typeName); if (isVarArg) { buffer.append("..."); } } buffer.append(' '); String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); buffer.append( parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; paramCount++; } index += md.params[i].stackSize; } buffer.append(')'); StructExceptionsAttribute attr = (StructExceptionsAttribute) mt.getAttributes().getWithKey("Exceptions"); if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { throwsExceptions = true; buffer.append(" throws "); for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { if (i > 0) { buffer.append(", "); } if (descriptor != null && !descriptor.exceptions.isEmpty()) { GenericType type = descriptor.exceptions.get(i); buffer.append(GenericMain.getGenericCastTypeName(type)); } else { VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true); buffer.append(ExprProcessor.getCastTypeName(type)); } } } } tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method)); if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) if (isAnnotation) { StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute) mt.getAttributes().getWithKey("AnnotationDefault"); if (attr != null) { buffer.append(" default "); buffer.append( attr.getDefaultValue() .toJava(indent + 1, new BytecodeMappingTracer())); // dummy tracer } } buffer.append(';'); buffer.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } else { if (!clinit && !dinit) { buffer.append(' '); } // We do not have line information for method start, lets have it here for now StructLineNumberTableAttribute lineNumberTable = (StructLineNumberTableAttribute) mt.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE); if (lineNumberTable != null && DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) { buffer.setCurrentLine(lineNumberTable.getFirstLine() - 1); } buffer.append('{').appendLineSeparator(); tracer.incrementCurrentSourceLine(); RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence try { int startLine = tracer.getCurrentSourceLine(); TextBuffer code = root.toJava(indent + 1, tracer); hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; if (!hideMethod && lineNumberTable != null && DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_LINE_NUMBERS)) { mapLines(code, lineNumberTable, tracer, startLine); } buffer.append(code); } catch (Throwable ex) { DecompilerContext.getLogger() .writeMessage( "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); methodWrapper.decompiledWithErrors = true; } } if (methodWrapper.decompiledWithErrors) { buffer.appendIndent(indent + 1); buffer.append("// $FF: Couldn't be decompiled"); buffer.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } if (root != null) { tracer.addMapping(root.getDummyExit().bytecode); } buffer.appendIndent(indent).append('}').appendLineSeparator(); tracer.incrementCurrentSourceLine(); } } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); } // save total lines // TODO: optimize // tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); return !hideMethod; }
private static void methodLambdaToJava( ClassNode lambdaNode, ClassWrapper classWrapper, StructMethod mt, TextBuffer buffer, int indent, boolean codeOnly, BytecodeMappingTracer tracer) { MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); MethodWrapper outerWrapper = (MethodWrapper) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); try { String method_name = lambdaNode.lambdaInformation.method_name; MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor); MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor); if (!codeOnly) { buffer.appendIndent(indent); buffer.append("public "); buffer.append(method_name); buffer.append("("); boolean firstParameter = true; int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1; int start_index = md_content.params.length - md_lambda.params.length; for (int i = 0; i < md_content.params.length; i++) { if (i >= start_index) { if (!firstParameter) { buffer.append(", "); } String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy()); if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && DecompilerContext.getOption( IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } buffer.append(typeName); buffer.append(" "); String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); buffer.append( parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; } index += md_content.params[i].stackSize; } buffer.append(") {").appendLineSeparator(); indent += 1; } RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if (!methodWrapper.decompiledWithErrors) { if (root != null) { // check for existence try { buffer.append(root.toJava(indent, tracer)); } catch (Throwable ex) { DecompilerContext.getLogger() .writeMessage( "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); methodWrapper.decompiledWithErrors = true; } } } if (methodWrapper.decompiledWithErrors) { buffer.appendIndent(indent); buffer.append("// $FF: Couldn't be decompiled"); buffer.appendLineSeparator(); } if (root != null) { tracer.addMapping(root.getDummyExit().bytecode); } if (!codeOnly) { indent -= 1; buffer.appendIndent(indent).append('}').appendLineSeparator(); } } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); } }