Logo Search packages:      
Sourcecode: yui-compressor version File versions  Download package

static String org::mozilla::javascript::Decompiler::decompile ( String  source,
int  flags,
UintMap  properties 
) [inline, static]

Decompile the source information associated with this js function/script back into a string. For the most part, this just means translating tokens back to their string representations; there's a little bit of lookahead logic to decide the proper spacing/indentation. Most of the work in mapping the original source to the prettyprinted decompiled version is done by the parser.

Parameters:
source encoded source tree presentation
flags flags to select output format
properties indentation properties

Definition at line 289 of file Decompiler.java.

References CASE_GAP_PROP, INDENT_GAP_PROP, and INITIAL_INDENT_PROP.

    {
        int length = source.length();
        if (length == 0) { return ""; }

        int indent = properties.getInt(INITIAL_INDENT_PROP, 0);
        if (indent < 0) throw new IllegalArgumentException();
        int indentGap = properties.getInt(INDENT_GAP_PROP, 4);
        if (indentGap < 0) throw new IllegalArgumentException();
        int caseGap = properties.getInt(CASE_GAP_PROP, 2);
        if (caseGap < 0) throw new IllegalArgumentException();

        StringBuffer result = new StringBuffer();
        boolean justFunctionBody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
        boolean toSource = (0 != (flags & Decompiler.TO_SOURCE_FLAG));

        // Spew tokens in source, for debugging.
        // as TYPE number char
        if (printSource) {
            System.err.println("length:" + length);
            for (int i = 0; i < length; ++i) {
                // Note that tokenToName will fail unless Context.printTrees
                // is true.
                String tokenname = null;
                if (Token.printNames) {
                    tokenname = Token.name(source.charAt(i));
                }
                if (tokenname == null) {
                    tokenname = "---";
                }
                String pad = tokenname.length() > 7
                    ? "\t"
                    : "\t\t";
                System.err.println
                    (tokenname
                     + pad + (int)source.charAt(i)
                     + "\t'" + ScriptRuntime.escapeString
                     (source.substring(i, i+1))
                     + "'");
            }
            System.err.println();
        }

        int braceNesting = 0;
        boolean afterFirstEOL = false;
        int i = 0;
        int topFunctionType;
        if (source.charAt(i) == Token.SCRIPT) {
            ++i;
            topFunctionType = -1;
        } else {
            topFunctionType = source.charAt(i + 1);
        }

        if (!toSource) {
            // add an initial newline to exactly match js.
            result.append('\n');
            for (int j = 0; j < indent; j++)
                result.append(' ');
        } else {
            if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
                result.append('(');
            }
        }

        while (i < length) {
            switch(source.charAt(i)) {
            case Token.GET:
            case Token.SET:
                result.append(source.charAt(i) == Token.GET ? "get " : "set ");
                ++i;
                i = printSourceString(source, i + 1, false, result);
                // Now increment one more to get past the FUNCTION token
                ++i;
                break;

            case Token.NAME:
            case Token.REGEXP:  // re-wrapped in '/'s in parser...
                i = printSourceString(source, i + 1, false, result);
                continue;

            case Token.STRING:
                i = printSourceString(source, i + 1, true, result);
                continue;

            case Token.NUMBER:
                i = printSourceNumber(source, i + 1, result);
                continue;

            case Token.TRUE:
                result.append("true");
                break;

            case Token.FALSE:
                result.append("false");
                break;

            case Token.NULL:
                result.append("null");
                break;

            case Token.THIS:
                result.append("this");
                break;

            case Token.FUNCTION:
                ++i; // skip function type
                result.append("function ");
                break;

            case FUNCTION_END:
                // Do nothing
                break;

            case Token.COMMA:
                result.append(", ");
                break;

            case Token.LC:
                ++braceNesting;
                if (Token.EOL == getNext(source, length, i))
                    indent += indentGap;
                result.append('{');
                break;

            case Token.RC: {
                --braceNesting;
                /* don't print the closing RC if it closes the
                 * toplevel function and we're called from
                 * decompileFunctionBody.
                 */
                if (justFunctionBody && braceNesting == 0)
                    break;

                result.append('}');
                switch (getNext(source, length, i)) {
                    case Token.EOL:
                    case FUNCTION_END:
                        indent -= indentGap;
                        break;
                    case Token.WHILE:
                    case Token.ELSE:
                        indent -= indentGap;
                        result.append(' ');
                        break;
                }
                break;
            }
            case Token.LP:
                result.append('(');
                break;

            case Token.RP:
                result.append(')');
                if (Token.LC == getNext(source, length, i))
                    result.append(' ');
                break;

            case Token.LB:
                result.append('[');
                break;

            case Token.RB:
                result.append(']');
                break;

            case Token.EOL: {
                if (toSource) break;
                boolean newLine = true;
                if (!afterFirstEOL) {
                    afterFirstEOL = true;
                    if (justFunctionBody) {
                        /* throw away just added 'function name(...) {'
                         * and restore the original indent
                         */
                        result.setLength(0);
                        indent -= indentGap;
                        newLine = false;
                    }
                }
                if (newLine) {
                    result.append('\n');
                }

                /* add indent if any tokens remain,
                 * less setback if next token is
                 * a label, case or default.
                 */
                if (i + 1 < length) {
                    int less = 0;
                    int nextToken = source.charAt(i + 1);
                    if (nextToken == Token.CASE
                        || nextToken == Token.DEFAULT)
                    {
                        less = indentGap - caseGap;
                    } else if (nextToken == Token.RC) {
                        less = indentGap;
                    }

                    /* elaborate check against label... skip past a
                     * following inlined NAME and look for a COLON.
                     */
                    else if (nextToken == Token.NAME) {
                        int afterName = getSourceStringEnd(source, i + 2);
                        if (source.charAt(afterName) == Token.COLON)
                            less = indentGap;
                    }

                    for (; less < indent; less++)
                        result.append(' ');
                }
                break;
            }
            case Token.DOT:
                result.append('.');
                break;

            case Token.NEW:
                result.append("new ");
                break;

            case Token.DELPROP:
                result.append("delete ");
                break;

            case Token.IF:
                result.append("if ");
                break;

            case Token.ELSE:
                result.append("else ");
                break;

            case Token.FOR:
                result.append("for ");
                break;

            case Token.IN:
                result.append(" in ");
                break;

            case Token.WITH:
                result.append("with ");
                break;

            case Token.WHILE:
                result.append("while ");
                break;

            case Token.DO:
                result.append("do ");
                break;

            case Token.TRY:
                result.append("try ");
                break;

            case Token.CATCH:
                result.append("catch ");
                break;

            case Token.FINALLY:
                result.append("finally ");
                break;

            case Token.THROW:
                result.append("throw ");
                break;

            case Token.SWITCH:
                result.append("switch ");
                break;

            case Token.BREAK:
                result.append("break");
                if (Token.NAME == getNext(source, length, i))
                    result.append(' ');
                break;

            case Token.CONTINUE:
                result.append("continue");
                if (Token.NAME == getNext(source, length, i))
                    result.append(' ');
                break;

            case Token.CASE:
                result.append("case ");
                break;

            case Token.DEFAULT:
                result.append("default");
                break;

            case Token.RETURN:
                result.append("return");
                if (Token.SEMI != getNext(source, length, i))
                    result.append(' ');
                break;

            case Token.VAR:
                result.append("var ");
                break;

            case Token.LET:
              result.append("let ");
              break;

            case Token.SEMI:
                result.append(';');
                if (Token.EOL != getNext(source, length, i)) {
                    // separators in FOR
                    result.append(' ');
                }
                break;

            case Token.ASSIGN:
                result.append(" = ");
                break;

            case Token.ASSIGN_ADD:
                result.append(" += ");
                break;

            case Token.ASSIGN_SUB:
                result.append(" -= ");
                break;

            case Token.ASSIGN_MUL:
                result.append(" *= ");
                break;

            case Token.ASSIGN_DIV:
                result.append(" /= ");
                break;

            case Token.ASSIGN_MOD:
                result.append(" %= ");
                break;

            case Token.ASSIGN_BITOR:
                result.append(" |= ");
                break;

            case Token.ASSIGN_BITXOR:
                result.append(" ^= ");
                break;

            case Token.ASSIGN_BITAND:
                result.append(" &= ");
                break;

            case Token.ASSIGN_LSH:
                result.append(" <<= ");
                break;

            case Token.ASSIGN_RSH:
                result.append(" >>= ");
                break;

            case Token.ASSIGN_URSH:
                result.append(" >>>= ");
                break;

            case Token.HOOK:
                result.append(" ? ");
                break;

            case Token.OBJECTLIT:
                // pun OBJECTLIT to mean colon in objlit property
                // initialization.
                // This needs to be distinct from COLON in the general case
                // to distinguish from the colon in a ternary... which needs
                // different spacing.
                result.append(':');
                break;

            case Token.COLON:
                if (Token.EOL == getNext(source, length, i))
                    // it's the end of a label
                    result.append(':');
                else
                    // it's the middle part of a ternary
                    result.append(" : ");
                break;

            case Token.OR:
                result.append(" || ");
                break;

            case Token.AND:
                result.append(" && ");
                break;

            case Token.BITOR:
                result.append(" | ");
                break;

            case Token.BITXOR:
                result.append(" ^ ");
                break;

            case Token.BITAND:
                result.append(" & ");
                break;

            case Token.SHEQ:
                result.append(" === ");
                break;

            case Token.SHNE:
                result.append(" !== ");
                break;

            case Token.EQ:
                result.append(" == ");
                break;

            case Token.NE:
                result.append(" != ");
                break;

            case Token.LE:
                result.append(" <= ");
                break;

            case Token.LT:
                result.append(" < ");
                break;

            case Token.GE:
                result.append(" >= ");
                break;

            case Token.GT:
                result.append(" > ");
                break;

            case Token.INSTANCEOF:
                result.append(" instanceof ");
                break;

            case Token.LSH:
                result.append(" << ");
                break;

            case Token.RSH:
                result.append(" >> ");
                break;

            case Token.URSH:
                result.append(" >>> ");
                break;

            case Token.TYPEOF:
                result.append("typeof ");
                break;

            case Token.VOID:
                result.append("void ");
                break;

            case Token.CONST:
                result.append("const ");
                break;
                
            case Token.YIELD:
                result.append("yield ");
                break;
            
            case Token.NOT:
                result.append('!');
                break;

            case Token.BITNOT:
                result.append('~');
                break;

            case Token.POS:
                result.append('+');
                break;

            case Token.NEG:
                result.append('-');
                break;

            case Token.INC:
                result.append("++");
                break;

            case Token.DEC:
                result.append("--");
                break;

            case Token.ADD:
                result.append(" + ");
                break;

            case Token.SUB:
                result.append(" - ");
                break;

            case Token.MUL:
                result.append(" * ");
                break;

            case Token.DIV:
                result.append(" / ");
                break;

            case Token.MOD:
                result.append(" % ");
                break;

            case Token.COLONCOLON:
                result.append("::");
                break;

            case Token.DOTDOT:
                result.append("..");
                break;

            case Token.DOTQUERY:
                result.append(".(");
                break;

            case Token.XMLATTR:
                result.append('@');
                break;

            default:
                // If we don't know how to decompile it, raise an exception.
                throw new RuntimeException("Token: " +
                                               Token.name(source.charAt(i)));
            }
            ++i;
        }

        if (!toSource) {
            // add that trailing newline if it's an outermost function.
            if (!justFunctionBody)
                result.append('\n');
        } else {
            if (topFunctionType == FunctionNode.FUNCTION_EXPRESSION) {
                result.append(')');
            }
        }

        return result.toString();
    }


Generated by  Doxygen 1.6.0   Back to index