/** * Generates a printable string that represents the given ActionScript 3 code. * * @param resolvedCode The ActionScript 3 code to turn into a string. * @param code The raw ActionScript 3 code. * @param prefix The prefix to write before every printed line. * @return The generated ActionScript 3 code string. */ private static String getCodeText( final ResolvedCode resolvedCode, final AS3Code code, final String prefix) { if (code.getInstructions().size() == 0) { return ""; } final StringBuilder sb = new StringBuilder(); final AS3Instruction firstInstruction = code.getInstructions().get(0); final int firstOffset = firstInstruction.getBitPosition(); for (final AS3Instruction instruction : code.getInstructions()) { final int absoluteOffset = instruction.getBitPosition() / 8; final int relativeOffset = absoluteOffset - firstOffset / 8; sb.append(prefix); sb.append(String.format("%08X %08X ", absoluteOffset, relativeOffset)); addInstructionText(sb, instruction, resolvedCode, firstOffset / 8); sb.append('\n'); } return sb.toString(); }
/** * Adds the class header to the output. * * @param resolvedClass Provides the class information. * @param sb The string builder the output is appended to. */ private static void addClassHeader(final ResolvedClass resolvedClass, final StringBuilder sb) { sb.append("class "); // Add the class name. final String className = ActionScript3Helpers.flattenNamespaceName(resolvedClass.getName()); sb.append(className); // Add the opening class parentheses. sb.append("\n{\n"); }
/** * Resolves and adds an unsigned integer value instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the instruction. * @param uintIndex The unsigned integer index. * @param code The code the instruction belongs to. */ private static void addUInteger( final StringBuilder sb, final String mnemonic, final int uintIndex, final ResolvedCode code) { final Long integer = code.resolveUInteger(uintIndex - 1); sb.append(mnemonic); sb.append(" "); if (integer == null) { sb.append(String.format("??? [0x%08X]", uintIndex)); } else { sb.append(String.format("%d [0x%08X]", integer, uintIndex)); } }
/** * Resolves and adds a namespace instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the instruction. * @param namespace The namespace index. * @param code The code the instruction belongs to. */ private static void addNamespace( final StringBuilder sb, final String mnemonic, final int namespace, final ResolvedCode code) { final String namespaceString = code.resolveNamespace(namespace); sb.append(mnemonic); sb.append(" "); if (namespaceString == null) { sb.append(String.format("??? [0x%08X]", namespace)); } else { sb.append(String.format("\"%s\" [0x%08X]", namespaceString, namespace)); } }
/** * Resolves and adds a double value to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the instruction. * @param doubleIndex The double index. * @param code The code the instruction belongs to. */ private static void addDouble( final StringBuilder sb, final String mnemonic, final int doubleIndex, final ResolvedCode code) { final Double dbl = code.resolveDouble(doubleIndex - 1); sb.append(mnemonic); sb.append(" "); if (dbl == null) { sb.append(String.format("??? [0x%08X]", doubleIndex)); } else { sb.append(String.format("%f [0x%08X]", dbl, doubleIndex)); } }
/** * Resolves and adds a string value instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the instruction. * @param stringIndex The string index. * @param code The code the instruction belongs to. */ private static void addString( final StringBuilder sb, final String mnemonic, final int stringIndex, final ResolvedCode code) { final String string = code.resolveString(stringIndex - 1); sb.append(mnemonic); sb.append(" "); if (string == null) { sb.append(String.format("??? [0x%08X]", stringIndex)); } else { sb.append(String.format("\"%s\" [0x%08X]", string, stringIndex)); } }
/** * Resolves and adds an multiname instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the instruction. * @param multiName The multiname index. * @param code The code the instruction belongs to. */ private static void addMultiname( final StringBuilder sb, final String mnemonic, final int multiName, final ResolvedCode code) { final String[] multinameString = code.resolveMultiname(multiName); sb.append(mnemonic); sb.append(" "); if (multinameString == null) { sb.append(String.format("??? [0x%08X]", multiName)); } else { sb.append( String.format( "%s [0x%08X]", ActionScript3Helpers.flattenNamespaceName(multinameString), multiName)); } }
/** * Generates a printable string that represents all code in a given resolved ActionScript 3 code * snippet. * * @param code The resolved code to display. * @return The generated ActionScript 3 code string. */ public static String getCodeText(final ResolvedCode code) { final StringBuilder sb = new StringBuilder(); for (final ResolvedClass resolvedClass : code.getClasses()) { // Start out with the class definition. addClassHeader(resolvedClass, sb); boolean hadCode = false; // Start preparing all the methods for printing. Add the instance // constructor and the class constructor for printing. final ResolvedMethod staticConstructor = resolvedClass.getStaticConstructor(); final ResolvedMethod constructor = resolvedClass.getConstructor(); final List<ResolvedMethod> resolvedMethods = resolvedClass.getMethods(); resolvedMethods.add(0, constructor); if (staticConstructor.getCode() != null) { // Only add the static constructor if it actually has code. resolvedMethods.add(0, staticConstructor); } final String className = ActionScript3Helpers.flattenNamespaceName(resolvedClass.getName()); // Print the individual methods now. for (final ResolvedMethod resolvedMethod : resolvedMethods) { if (resolvedMethod != resolvedMethods.get(0)) { if (hadCode || resolvedMethod.getCode() != null) { sb.append("\n"); } } hadCode = addMethod(code, resolvedMethod, className, sb); } addClassFooter(sb); } return sb.toString(); }
/** * Adds the class footer to the output. * * @param sb The string builder the output is appended to. */ private static void addClassFooter(final StringBuilder sb) { sb.append("}\n\n"); }
/** * Adds an instruction with two arguments to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The instruction mnemonic. * @param value1 The first instruction argument value. * @param value2 The second instruction argument value. */ private static void add( final StringBuilder sb, final String mnemonic, final long value1, final long value2) { sb.append(String.format("%s %d, %d", mnemonic, value1, value2)); }
/** * Adds a simple one-mnemonic instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The instruction mnemonic. */ private static void add(final StringBuilder sb, final String mnemonic) { sb.append(mnemonic); }
/** * Adds an if instruction to the output. * * @param sb The string builder the output is appended to. * @param mnemonic The mnemonic of the if instruction. * @param value The branch offset value. */ private static void addIf(final StringBuilder sb, final String mnemonic, final long value) { sb.append(String.format("%s %08X", mnemonic, value)); }
/** * Adds a method to the output. * * @param code The code the method belongs to. * @param resolvedMethod The method to add to the output. * @param className The name of the enclosing class the method belongs to. * @param sb The string builder the output is appended to. * @return True, if the method has code. False, if the method is just a declaration. */ private static boolean addMethod( final ResolvedCode code, final ResolvedMethod resolvedMethod, final String className, final StringBuilder sb) { sb.append("\t"); final String flattenedReturnType = ActionScript3Helpers.flattenNamespaceName(resolvedMethod.getReturnType()); sb.append(flattenedReturnType); if (!flattenedReturnType.isEmpty()) { // This path is taken for all methods but constructors. sb.append(" "); } final String functionName = ActionScript3Helpers.flattenNamespaceName(resolvedMethod.getName()); if (functionName.startsWith(className)) { // To make code more readable, strip fully qualified type // information. sb.append(functionName.substring(className.length() + 1)); } else { sb.append(functionName); } sb.append("("); // Print the method arguments. for (final String[] argument : resolvedMethod.getArguments()) { if (argument != resolvedMethod.getArguments().get(0)) { sb.append(", "); } sb.append(ActionScript3Helpers.flattenNamespaceName(argument)); } sb.append(")"); // Print the code. final AS3Code methodCode = resolvedMethod.getCode(); if (methodCode == null) { sb.append(";\n"); return false; } else { sb.append("\n"); sb.append("\t{"); sb.append("\n"); sb.append(getCodeText(code, methodCode, "\t\t")); sb.append("\t}\n"); return true; } }