diff --git a/ASTree.cpp b/ASTree.cpp index c90d80e..2ce699a 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -15,9 +15,12 @@ static bool inPrint; /* Use this to prevent printing return keywords and newlines in lambdas. */ static bool inLambda = false; -/* Use this to keep track of whether we need to print out the list of global - * variables that we are using (such as inside a function). */ -static bool printGlobals = false; +/* Use this to keep track of whether we need to print out any docstring and + * the list of global variables that we are using (such as inside a function). */ +static bool printDocstringAndGlobals = false; + +/* Use this to keep track of whether we need to print a class or module docstring */ +static bool printClassDocstring = true; PycRef BuildFromCode(PycRef code, PycModule* mod) { @@ -2585,7 +2588,7 @@ void print_src(PycRef node, PycModule* mod) fprintf(pyc_output, ": "); } else { fprintf(pyc_output, "):\n"); - printGlobals = true; + printDocstringAndGlobals = true; } bool preLambda = inLambda; @@ -2614,6 +2617,7 @@ void print_src(PycRef node, PycModule* mod) // Don't put parens if there are no base classes fprintf(pyc_output, ":\n"); } + printClassDocstring = true; PycRef code = src.cast()->code().cast() ->func().cast()->code(); print_src(code, mod); @@ -2654,38 +2658,13 @@ void print_src(PycRef node, PycModule* mod) print_src(dest, mod); } } + } else if (src->type() == ASTNode::NODE_BINARY && + src.cast()->is_inplace() == true) { + print_src(src, mod); } else { - if (src->type() == ASTNode::NODE_BINARY && - src.cast()->is_inplace() == true) { - print_src(src, mod); - break; - } - - if (dest->type() == ASTNode::NODE_NAME && - dest.cast()->name()->isEqual("__doc__")) { - if (src->type() == ASTNode::NODE_OBJECT) { - PycRef obj = src.cast()->object(); - if (obj->type() == PycObject::TYPE_STRING) - OutputString(obj.cast(), (mod->majorVer() == 3) ? 'b' : 0, true); - else if (obj->type() == PycObject::TYPE_UNICODE) - OutputString(obj.cast(), (mod->majorVer() == 3) ? 0 : 'u', true); - else if (obj->type() == PycObject::TYPE_INTERNED || - obj->type() == PycObject::TYPE_STRINGREF || - obj->type() == PycObject::TYPE_ASCII || - obj->type() == PycObject::TYPE_ASCII_INTERNED || - obj->type() == PycObject::TYPE_SHORT_ASCII || - obj->type() == PycObject::TYPE_SHORT_ASCII_INTERNED) - OutputString(obj.cast(), 0, true); - } else { - print_src(dest, mod); - fprintf(pyc_output, " = "); - print_src(src, mod); - } - } else { - print_src(dest, mod); - fprintf(pyc_output, " = "); - print_src(src, mod); - } + print_src(dest, mod); + fprintf(pyc_output, " = "); + print_src(src, mod); } } break; @@ -2731,6 +2710,30 @@ void print_src(PycRef node, PycModule* mod) cleanBuild = true; } +bool print_docstring(PycRef obj, int indent, PycModule* mod) +{ + // docstrings are translated from the bytecode __doc__ = 'string' to simply '''string''' + char prefix = -1; + if (obj->type() == PycObject::TYPE_STRING) + prefix = mod->majorVer() == 3 ? 'b' : 0; + else if (obj->type() == PycObject::TYPE_UNICODE) + prefix = mod->majorVer() == 3 ? 0 : 'u'; + else if (obj->type() == PycObject::TYPE_INTERNED || + obj->type() == PycObject::TYPE_STRINGREF || + obj->type() == PycObject::TYPE_ASCII || + obj->type() == PycObject::TYPE_ASCII_INTERNED || + obj->type() == PycObject::TYPE_SHORT_ASCII || + obj->type() == PycObject::TYPE_SHORT_ASCII_INTERNED) + prefix = 0; + if (prefix != -1) { + start_line(indent); + OutputString(obj.cast(), prefix, true); + fprintf(pyc_output, "\n"); + return true; + } else + return false; +} + void decompyle(PycRef code, PycModule* mod) { PycRef source = BuildFromCode(code, mod); @@ -2754,6 +2757,17 @@ void decompyle(PycRef code, PycModule* mod) } } } + // Class and module docstrings may only appear at the beginning of their source + if (printClassDocstring && clean->nodes().front()->type() == ASTNode::NODE_STORE) { + PycRef store = clean->nodes().front().cast(); + if (store->dest()->type() == ASTNode::NODE_NAME && + store->dest().cast()->name()->isEqual("__doc__") && + store->src()->type() == ASTNode::NODE_OBJECT) { + if (print_docstring(store->src().cast()->object(), + cur_indent + (code->name()->isEqual("") ? 0 : 1), mod)) + clean->removeFirst(); + } + } if (clean->nodes().back()->type() == ASTNode::NODE_RETURN) { PycRef ret = clean->nodes().back().cast(); @@ -2762,6 +2776,8 @@ void decompyle(PycRef code, PycModule* mod) } } } + if (printClassDocstring) + printClassDocstring = false; // This is outside the clean check so a source block will always // be compilable, even if decompylation failed. if (clean->nodes().size() == 0) @@ -2770,20 +2786,25 @@ void decompyle(PycRef code, PycModule* mod) inPrint = false; bool part1clean = cleanBuild; - PycCode::globals_t globs = code->getGlobals(); - if (printGlobals && globs.size()) { - start_line(cur_indent+1); - fprintf(pyc_output, "global "); - bool first = true; - PycCode::globals_t::iterator it; - for (it = globs.begin(); it != globs.end(); ++it) { - if (!first) - fprintf(pyc_output, ", "); - fprintf(pyc_output, "%s", (*it)->value()); - first = false; + if (printDocstringAndGlobals) { + if (code->consts()->size()) + print_docstring(code->getConst(0), cur_indent + 1, mod); + + PycCode::globals_t globs = code->getGlobals(); + if (globs.size()) { + start_line(cur_indent + 1); + fprintf(pyc_output, "global "); + bool first = true; + PycCode::globals_t::iterator it; + for (it = globs.begin(); it != globs.end(); ++it) { + if (!first) + fprintf(pyc_output, ", "); + fprintf(pyc_output, "%s", (*it)->value()); + first = false; + } + fprintf(pyc_output, "\n"); } - fprintf(pyc_output, "\n"); - printGlobals = false; + printDocstringAndGlobals = false; } print_src(source, mod);