Compare commits
23 Commits
v0.1.0
...
a05ddec0d8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a05ddec0d8 | ||
![]() |
d8c6fdf711 | ||
![]() |
577720302e | ||
![]() |
38799f5cfb | ||
![]() |
0e7be40367 | ||
![]() |
ff0c1450b4 | ||
![]() |
e8e10f1419 | ||
![]() |
a267bfb47f | ||
![]() |
8b0ea9450e | ||
![]() |
7d2039d24e | ||
![]() |
e64ea4bdec | ||
![]() |
4badfa6321 | ||
![]() |
6e0089e01c | ||
![]() |
3afcfbc6a7 | ||
![]() |
aa292c7682 | ||
![]() |
5fe61462a2 | ||
![]() |
ad5f39db56 | ||
![]() |
97ec04789d | ||
![]() |
6dae4e801f | ||
![]() |
040732920b | ||
![]() |
a93fd14672 | ||
![]() |
a4a6a24f3e | ||
![]() |
307e0b8fd7 |
2
.github/workflows/msvc-ci.yml
vendored
2
.github/workflows/msvc-ci.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
cmake --build build --config Release --target check
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pycdc-release
|
||||
path: build\Release\*.exe
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@
|
||||
/.kdev4
|
||||
__pycache__
|
||||
tests-out
|
||||
build/
|
||||
.vscode/
|
||||
|
153
ASTree.cpp
153
ASTree.cpp
@@ -1,6 +1,7 @@
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include "ASTree.h"
|
||||
#include "FastStack.h"
|
||||
#include "pyc_numeric.h"
|
||||
@@ -897,7 +898,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
case Pyc::INSTRUMENTED_FOR_ITER_A:
|
||||
{
|
||||
PycRef<ASTNode> iter = stack.top(); // Iterable
|
||||
stack.pop();
|
||||
if (mod->verCompare(3, 12) < 0) {
|
||||
// Do not pop the iterator for py 3.12+
|
||||
stack.pop();
|
||||
}
|
||||
/* Pop it? Don't pop it? */
|
||||
|
||||
int end;
|
||||
@@ -1163,6 +1167,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
}
|
||||
break;
|
||||
case Pyc::JUMP_ABSOLUTE_A:
|
||||
// bpo-47120: Replaced JUMP_ABSOLUTE by the relative jump JUMP_BACKWARD.
|
||||
case Pyc::JUMP_BACKWARD_A:
|
||||
case Pyc::JUMP_BACKWARD_NO_INTERRUPT_A:
|
||||
{
|
||||
int offs = operand;
|
||||
if (mod->verCompare(3, 10) >= 0)
|
||||
@@ -1225,8 +1232,12 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
break;
|
||||
}
|
||||
|
||||
stack = stack_hist.top();
|
||||
stack_hist.pop();
|
||||
if (!stack_hist.empty()) {
|
||||
stack = stack_hist.top();
|
||||
stack_hist.pop();
|
||||
} else {
|
||||
fprintf(stderr, "Warning: Stack history is empty, something wrong might have happened\n");
|
||||
}
|
||||
|
||||
PycRef<ASTBlock> prev = curblock;
|
||||
PycRef<ASTBlock> nil;
|
||||
@@ -1383,10 +1394,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
|
||||
} while (prev != nil);
|
||||
|
||||
curblock = blocks.top();
|
||||
|
||||
if (curblock->blktype() == ASTBlock::BLK_EXCEPT) {
|
||||
curblock->setEnd(pos+offs);
|
||||
if (!blocks.empty()) {
|
||||
curblock = blocks.top();
|
||||
if (curblock->blktype() == ASTBlock::BLK_EXCEPT)
|
||||
curblock->setEnd(pos+offs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1678,10 +1689,37 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
case Pyc::POP_EXCEPT:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case Pyc::END_FOR:
|
||||
{
|
||||
stack.pop();
|
||||
|
||||
if ((opcode == Pyc::END_FOR) && (mod->majorVer() == 3) && (mod->minorVer() == 12)) {
|
||||
// one additional pop for python 3.12
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
// end for loop here
|
||||
/* TODO : Ensure that FOR loop ends here.
|
||||
Due to CACHE instructions at play, the end indicated in
|
||||
the for loop by pycdas is not correct, it is off by
|
||||
some small amount. */
|
||||
if (curblock->blktype() == ASTBlock::BLK_FOR) {
|
||||
PycRef<ASTBlock> prev = blocks.top();
|
||||
blocks.pop();
|
||||
|
||||
curblock = blocks.top();
|
||||
curblock->append(prev.cast<ASTNode>());
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Wrong block type %i for END_FOR\n", curblock->blktype());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Pyc::POP_TOP:
|
||||
{
|
||||
PycRef<ASTNode> value = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (!curblock->inited()) {
|
||||
if (curblock->blktype() == ASTBlock::BLK_WITH) {
|
||||
curblock.cast<ASTWithBlock>()->setExpr(value);
|
||||
@@ -1736,7 +1774,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
else
|
||||
curblock->append(new ASTPrint(stack.top(), stream));
|
||||
stack.pop();
|
||||
stream->setProcessed();
|
||||
if (stream)
|
||||
stream->setProcessed();
|
||||
}
|
||||
break;
|
||||
case Pyc::PRINT_NEWLINE:
|
||||
@@ -1764,7 +1803,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
else
|
||||
curblock->append(new ASTPrint(nullptr, stream));
|
||||
stack.pop();
|
||||
stream->setProcessed();
|
||||
if (stream)
|
||||
stream->setProcessed();
|
||||
}
|
||||
break;
|
||||
case Pyc::RAISE_VARARGS_A:
|
||||
@@ -1787,8 +1827,6 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
blocks.pop();
|
||||
curblock = blocks.top();
|
||||
curblock->append(prev.cast<ASTNode>());
|
||||
|
||||
bc_next(source, mod, opcode, operand, pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2473,6 +2511,79 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
|
||||
stack.push(next_tup);
|
||||
}
|
||||
break;
|
||||
case Pyc::BINARY_SLICE:
|
||||
{
|
||||
PycRef<ASTNode> end = stack.top();
|
||||
stack.pop();
|
||||
PycRef<ASTNode> start = stack.top();
|
||||
stack.pop();
|
||||
PycRef<ASTNode> dest = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (start.type() == ASTNode::NODE_OBJECT
|
||||
&& start.cast<ASTObject>()->object() == Pyc_None) {
|
||||
start = NULL;
|
||||
}
|
||||
|
||||
if (end.type() == ASTNode::NODE_OBJECT
|
||||
&& end.cast<ASTObject>()->object() == Pyc_None) {
|
||||
end = NULL;
|
||||
}
|
||||
|
||||
PycRef<ASTNode> slice;
|
||||
if (start == NULL && end == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE0);
|
||||
} else if (start == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE2, start, end);
|
||||
} else if (end == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE1, start, end);
|
||||
} else {
|
||||
slice = new ASTSlice(ASTSlice::SLICE3, start, end);
|
||||
}
|
||||
stack.push(new ASTSubscr(dest, slice));
|
||||
}
|
||||
break;
|
||||
case Pyc::STORE_SLICE:
|
||||
{
|
||||
PycRef<ASTNode> end = stack.top();
|
||||
stack.pop();
|
||||
PycRef<ASTNode> start = stack.top();
|
||||
stack.pop();
|
||||
PycRef<ASTNode> dest = stack.top();
|
||||
stack.pop();
|
||||
PycRef<ASTNode> values = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (start.type() == ASTNode::NODE_OBJECT
|
||||
&& start.cast<ASTObject>()->object() == Pyc_None) {
|
||||
start = NULL;
|
||||
}
|
||||
|
||||
if (end.type() == ASTNode::NODE_OBJECT
|
||||
&& end.cast<ASTObject>()->object() == Pyc_None) {
|
||||
end = NULL;
|
||||
}
|
||||
|
||||
PycRef<ASTNode> slice;
|
||||
if (start == NULL && end == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE0);
|
||||
} else if (start == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE2, start, end);
|
||||
} else if (end == NULL) {
|
||||
slice = new ASTSlice(ASTSlice::SLICE1, start, end);
|
||||
} else {
|
||||
slice = new ASTSlice(ASTSlice::SLICE3, start, end);
|
||||
}
|
||||
|
||||
curblock->append(new ASTStore(values, new ASTSubscr(dest, slice)));
|
||||
}
|
||||
break;
|
||||
case Pyc::COPY_A:
|
||||
{
|
||||
PycRef<ASTNode> value = stack.top(operand);
|
||||
stack.push(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported opcode: %s (%d)\n", Pyc::OpcodeName(opcode), opcode);
|
||||
cleanBuild = false;
|
||||
@@ -2669,6 +2780,8 @@ void print_formatted_value(PycRef<ASTFormattedValue> formatted_value, PycModule*
|
||||
pyc_output << "}";
|
||||
}
|
||||
|
||||
static std::unordered_set<ASTNode *> node_seen;
|
||||
|
||||
void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
|
||||
{
|
||||
if (node == NULL) {
|
||||
@@ -2677,6 +2790,12 @@ void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
|
||||
return;
|
||||
}
|
||||
|
||||
if (node_seen.find((ASTNode *)node) != node_seen.end()) {
|
||||
fputs("WARNING: Circular reference detected\n", stderr);
|
||||
return;
|
||||
}
|
||||
node_seen.insert((ASTNode *)node);
|
||||
|
||||
switch (node->type()) {
|
||||
case ASTNode::NODE_BINARY:
|
||||
case ASTNode::NODE_COMPARE:
|
||||
@@ -3332,10 +3451,12 @@ void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
|
||||
pyc_output << "<NODE:" << node->type() << ">";
|
||||
fprintf(stderr, "Unsupported Node type: %d\n", node->type());
|
||||
cleanBuild = false;
|
||||
node_seen.erase((ASTNode *)node);
|
||||
return;
|
||||
}
|
||||
|
||||
cleanBuild = true;
|
||||
node_seen.erase((ASTNode *)node);
|
||||
}
|
||||
|
||||
bool print_docstring(PycRef<PycObject> obj, int indent, PycModule* mod,
|
||||
@@ -3352,8 +3473,16 @@ bool print_docstring(PycRef<PycObject> obj, int indent, PycModule* mod,
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::unordered_set<PycCode *> code_seen;
|
||||
|
||||
void decompyle(PycRef<PycCode> code, PycModule* mod, std::ostream& pyc_output)
|
||||
{
|
||||
if (code_seen.find((PycCode *)code) != code_seen.end()) {
|
||||
fputs("WARNING: Circular reference detected\n", stderr);
|
||||
return;
|
||||
}
|
||||
code_seen.insert((PycCode *)code);
|
||||
|
||||
PycRef<ASTNode> source = BuildFromCode(code, mod);
|
||||
|
||||
PycRef<ASTNodeList> clean = source.cast<ASTNodeList>();
|
||||
@@ -3447,4 +3576,6 @@ void decompyle(PycRef<PycCode> code, PycModule* mod, std::ostream& pyc_output)
|
||||
start_line(cur_indent, pyc_output);
|
||||
pyc_output << "# WARNING: Decompyle incomplete\n";
|
||||
}
|
||||
|
||||
code_seen.erase((PycCode *)code);
|
||||
}
|
||||
|
24
FastStack.h
24
FastStack.h
@@ -30,14 +30,30 @@ public:
|
||||
{
|
||||
if (m_ptr > -1)
|
||||
m_stack[m_ptr--] = nullptr;
|
||||
else {
|
||||
#ifdef BLOCK_DEBUG
|
||||
fprintf(stderr, "pop from empty stack\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
PycRef<ASTNode> top() const
|
||||
PycRef<ASTNode> top(int i = 1) const
|
||||
{
|
||||
if (m_ptr > -1)
|
||||
return m_stack[m_ptr];
|
||||
else
|
||||
if (i > 0) {
|
||||
int idx = m_ptr + 1 - i;
|
||||
if ((m_ptr > -1) && (idx >= 0))
|
||||
return m_stack[idx];
|
||||
else {
|
||||
#ifdef BLOCK_DEBUG
|
||||
fprintf(stderr, "insufficient values on stack\n");
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "incorrect operand %i\n", i);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
|
19
bytecode.cpp
19
bytecode.cpp
@@ -472,6 +472,10 @@ void bc_disasm(std::ostream& pyc_output, PycRef<PycCode> code, PycModule* mod,
|
||||
case Pyc::INSTRUMENTED_POP_JUMP_IF_FALSE_A:
|
||||
case Pyc::INSTRUMENTED_POP_JUMP_IF_TRUE_A:
|
||||
{
|
||||
/* TODO: Fix offset based on CACHE instructions.
|
||||
Offset is relative to next non-CACHE instruction
|
||||
and thus will be printed lower than actual value.
|
||||
See TODO @ END_FOR ASTree.cpp */
|
||||
int offs = operand;
|
||||
if (mod->verCompare(3, 10) >= 0)
|
||||
offs *= sizeof(uint16_t); // BPO-27129
|
||||
@@ -596,3 +600,18 @@ void bc_disasm(std::ostream& pyc_output, PycRef<PycCode> code, PycModule* mod,
|
||||
pyc_output << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void bc_exceptiontable(std::ostream& pyc_output, PycRef<PycCode> code,
|
||||
int indent)
|
||||
{
|
||||
for (const auto& entry : code->exceptionTableEntries()) {
|
||||
|
||||
for (int i=0; i<indent; i++)
|
||||
pyc_output << " ";
|
||||
|
||||
pyc_output << entry.start_offset << " to " << entry.end_offset
|
||||
<< " -> " << entry.target << " [" << entry.stack_depth
|
||||
<< "] " << (entry.push_lasti ? "lasti": "")
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
@@ -32,3 +32,5 @@ void print_const(std::ostream& pyc_output, PycRef<PycObject> obj, PycModule* mod
|
||||
void bc_next(PycBuffer& source, PycModule* mod, int& opcode, int& operand, int& pos);
|
||||
void bc_disasm(std::ostream& pyc_output, PycRef<PycCode> code, PycModule* mod,
|
||||
int indent, unsigned flags);
|
||||
void bc_exceptiontable(std::ostream& pyc_output, PycRef<PycCode> code,
|
||||
int indent);
|
||||
|
28
data.cpp
28
data.cpp
@@ -53,35 +53,43 @@ bool PycFile::atEof() const
|
||||
int PycFile::getByte()
|
||||
{
|
||||
int ch = fgetc(m_stream);
|
||||
if (ch == EOF)
|
||||
ungetc(ch, m_stream);
|
||||
if (ch == EOF) {
|
||||
fputs("PycFile::getByte(): Unexpected end of stream\n", stderr);
|
||||
std::exit(1);
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
int PycFile::getBuffer(int bytes, void* buffer)
|
||||
void PycFile::getBuffer(int bytes, void* buffer)
|
||||
{
|
||||
return (int)fread(buffer, 1, bytes, m_stream);
|
||||
if (fread(buffer, 1, bytes, m_stream) != (size_t)bytes) {
|
||||
fputs("PycFile::getBuffer(): Unexpected end of stream\n", stderr);
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* PycBuffer */
|
||||
int PycBuffer::getByte()
|
||||
{
|
||||
if (atEof())
|
||||
return EOF;
|
||||
if (atEof()) {
|
||||
fputs("PycBuffer::getByte(): Unexpected end of stream\n", stderr);
|
||||
std::exit(1);
|
||||
}
|
||||
int ch = (int)(*(m_buffer + m_pos));
|
||||
++m_pos;
|
||||
return ch & 0xFF; // Make sure it's just a byte!
|
||||
}
|
||||
|
||||
int PycBuffer::getBuffer(int bytes, void* buffer)
|
||||
void PycBuffer::getBuffer(int bytes, void* buffer)
|
||||
{
|
||||
if (m_pos + bytes > m_size)
|
||||
bytes = m_size - m_pos;
|
||||
if (m_pos + bytes > m_size) {
|
||||
fputs("PycBuffer::getBuffer(): Unexpected end of stream\n", stderr);
|
||||
std::exit(1);
|
||||
}
|
||||
if (bytes != 0)
|
||||
memcpy(buffer, (m_buffer + m_pos), bytes);
|
||||
m_pos += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int formatted_print(std::ostream& stream, const char* format, ...)
|
||||
|
6
data.h
6
data.h
@@ -19,7 +19,7 @@ public:
|
||||
virtual bool atEof() const = 0;
|
||||
|
||||
virtual int getByte() = 0;
|
||||
virtual int getBuffer(int bytes, void* buffer) = 0;
|
||||
virtual void getBuffer(int bytes, void* buffer) = 0;
|
||||
int get16();
|
||||
int get32();
|
||||
Pyc_INT64 get64();
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
bool atEof() const override;
|
||||
|
||||
int getByte() override;
|
||||
int getBuffer(int bytes, void* buffer) override;
|
||||
void getBuffer(int bytes, void* buffer) override;
|
||||
|
||||
private:
|
||||
FILE* m_stream;
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
bool atEof() const override { return (m_pos == m_size); }
|
||||
|
||||
int getByte() override;
|
||||
int getBuffer(int bytes, void* buffer) override;
|
||||
void getBuffer(int bytes, void* buffer) override;
|
||||
|
||||
private:
|
||||
const unsigned char* m_buffer;
|
||||
|
41
pyc_code.cpp
41
pyc_code.cpp
@@ -128,3 +128,44 @@ PycRef<PycString> PycCode::getCellVar(PycModule* mod, int idx) const
|
||||
? m_freeVars->get(idx - m_cellVars->size()).cast<PycString>()
|
||||
: m_cellVars->get(idx).cast<PycString>();
|
||||
}
|
||||
|
||||
int _parse_varint(PycBuffer& data, int& pos) {
|
||||
int b = data.getByte();
|
||||
pos += 1;
|
||||
|
||||
int val = b & 0x3F;
|
||||
while (b & 0x40) {
|
||||
val <<= 6;
|
||||
|
||||
b = data.getByte();
|
||||
pos += 1;
|
||||
|
||||
val |= (b & 0x3F);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
std::vector<PycExceptionTableEntry> PycCode::exceptionTableEntries() const
|
||||
{
|
||||
PycBuffer data(m_exceptTable->value(), m_exceptTable->length());
|
||||
|
||||
std::vector<PycExceptionTableEntry> entries;
|
||||
|
||||
int pos = 0;
|
||||
while (!data.atEof()) {
|
||||
|
||||
int start = _parse_varint(data, pos) * 2;
|
||||
int length = _parse_varint(data, pos) * 2;
|
||||
int end = start + length;
|
||||
|
||||
int target = _parse_varint(data, pos) * 2;
|
||||
int dl = _parse_varint(data, pos);
|
||||
|
||||
int depth = dl >> 1;
|
||||
bool lasti = bool(dl & 1);
|
||||
|
||||
entries.push_back(PycExceptionTableEntry(start, end, target, depth, lasti));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
14
pyc_code.h
14
pyc_code.h
@@ -8,6 +8,18 @@
|
||||
class PycData;
|
||||
class PycModule;
|
||||
|
||||
class PycExceptionTableEntry {
|
||||
public:
|
||||
int start_offset; // inclusive
|
||||
int end_offset; // exclusive
|
||||
int target;
|
||||
int stack_depth;
|
||||
bool push_lasti;
|
||||
|
||||
PycExceptionTableEntry(int m_start_offset, int m_end_offset, int m_target, int m_stack_depth, bool m_push_lasti) :
|
||||
start_offset(m_start_offset), end_offset(m_end_offset), target(m_target), stack_depth(m_stack_depth), push_lasti(m_push_lasti) {};
|
||||
};
|
||||
|
||||
class PycCode : public PycObject {
|
||||
public:
|
||||
typedef std::vector<PycRef<PycString>> globals_t;
|
||||
@@ -87,6 +99,8 @@ public:
|
||||
m_globalsUsed.emplace_back(std::move(varname));
|
||||
}
|
||||
|
||||
std::vector<PycExceptionTableEntry> exceptionTableEntries() const;
|
||||
|
||||
private:
|
||||
int m_argCount, m_posOnlyArgCount, m_kwOnlyArgCount, m_numLocals;
|
||||
int m_stackSize, m_flags;
|
||||
|
21
pycdas.cpp
21
pycdas.cpp
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
#include "pyc_module.h"
|
||||
#include "pyc_numeric.h"
|
||||
#include "bytecode.h"
|
||||
@@ -73,6 +74,8 @@ static void iprintf(std::ostream& pyc_output, int indent, const char* fmt, ...)
|
||||
va_end(varargs);
|
||||
}
|
||||
|
||||
static std::unordered_set<PycObject *> out_seen;
|
||||
|
||||
void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
|
||||
unsigned flags, std::ostream& pyc_output)
|
||||
{
|
||||
@@ -81,6 +84,12 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
|
||||
return;
|
||||
}
|
||||
|
||||
if (out_seen.find((PycObject *)obj) != out_seen.end()) {
|
||||
fputs("WARNING: Circular reference detected\n", stderr);
|
||||
return;
|
||||
}
|
||||
out_seen.insert((PycObject *)obj);
|
||||
|
||||
switch (obj->type()) {
|
||||
case PycObject::TYPE_CODE:
|
||||
case PycObject::TYPE_CODE2:
|
||||
@@ -145,16 +154,16 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
|
||||
iputs(pyc_output, indent + 1, "[Disassembly]\n");
|
||||
bc_disasm(pyc_output, codeObj, mod, indent + 2, flags);
|
||||
|
||||
if (mod->verCompare(3, 11) >= 0) {
|
||||
iputs(pyc_output, indent + 1, "[Exception Table]\n");
|
||||
bc_exceptiontable(pyc_output, codeObj, indent+2);
|
||||
}
|
||||
|
||||
if (mod->verCompare(1, 5) >= 0 && (flags & Pyc::DISASM_PYCODE_VERBOSE) != 0) {
|
||||
iprintf(pyc_output, indent + 1, "First Line: %d\n", codeObj->firstLine());
|
||||
iputs(pyc_output, indent + 1, "[Line Number Table]\n");
|
||||
output_object(codeObj->lnTable().cast<PycObject>(), mod, indent + 2, flags, pyc_output);
|
||||
}
|
||||
|
||||
if (mod->verCompare(3, 11) >= 0 && (flags & Pyc::DISASM_PYCODE_VERBOSE) != 0) {
|
||||
iputs(pyc_output, indent + 1, "[Exception Table]\n");
|
||||
output_object(codeObj->exceptTable().cast<PycObject>(), mod, indent + 2, flags, pyc_output);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PycObject::TYPE_STRING:
|
||||
@@ -246,6 +255,8 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
|
||||
default:
|
||||
iprintf(pyc_output, indent, "<TYPE: %d>\n", obj->type());
|
||||
}
|
||||
|
||||
out_seen.erase((PycObject *)obj);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
BIN
tests/compiled/binary_slice.3.12.pyc
Normal file
BIN
tests/compiled/binary_slice.3.12.pyc
Normal file
Binary file not shown.
BIN
tests/compiled/store_slice.3.12.pyc
Normal file
BIN
tests/compiled/store_slice.3.12.pyc
Normal file
Binary file not shown.
BIN
tests/compiled/test_loops3.3.12.pyc
Normal file
BIN
tests/compiled/test_loops3.3.12.pyc
Normal file
Binary file not shown.
BIN
tests/compiled/test_raise_varargs.3.12.pyc
Normal file
BIN
tests/compiled/test_raise_varargs.3.12.pyc
Normal file
Binary file not shown.
7
tests/input/binary_slice.py
Normal file
7
tests/input/binary_slice.py
Normal file
@@ -0,0 +1,7 @@
|
||||
l = [1,2,3,4,5,6]
|
||||
print(l[1:3])
|
||||
print(l[:2])
|
||||
print(l[3:])
|
||||
print(l[-4:])
|
||||
print(l[:-2])
|
||||
print(l[:])
|
8
tests/input/store_slice.py
Normal file
8
tests/input/store_slice.py
Normal file
@@ -0,0 +1,8 @@
|
||||
a = [0] * 16
|
||||
l = [1,2,3]
|
||||
a[13:] = l
|
||||
a[5] = 10
|
||||
a[:2] = [1,2]
|
||||
a[:] = range(16)
|
||||
|
||||
print(a)
|
34
tests/input/test_loops3.py
Normal file
34
tests/input/test_loops3.py
Normal file
@@ -0,0 +1,34 @@
|
||||
def loop1():
|
||||
iterable = [1, 2, 3]
|
||||
for item in iterable:
|
||||
pass
|
||||
|
||||
loop1()
|
||||
|
||||
def loop2():
|
||||
for i in range(2):
|
||||
print(i)
|
||||
|
||||
loop2()
|
||||
|
||||
def loop3():
|
||||
def loop():
|
||||
x = (1,2,3)
|
||||
l = []
|
||||
for i in x:
|
||||
l.append(i)
|
||||
return l
|
||||
|
||||
return loop()
|
||||
|
||||
loop3()
|
||||
|
||||
def loop4():
|
||||
for i in range(3):
|
||||
for j in range(2):
|
||||
print(i*j)
|
||||
|
||||
loop4()
|
||||
|
||||
for j in [1,2,3][::-1]:
|
||||
print("hi", j)
|
7
tests/input/test_raise_varargs.py
Normal file
7
tests/input/test_raise_varargs.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import struct
|
||||
|
||||
def bytes_to_words(b):
|
||||
'''Convert a byte string (little-endian) to a list of 32-bit words.'''
|
||||
if len(b) % 4 != 0:
|
||||
raise ValueError('Input bytes length must be a multiple of 4 for word conversion.')
|
||||
return struct.unpack('<' + 'I' * (len(b) // 4), b)
|
7
tests/tokenized/binary_slice.txt
Normal file
7
tests/tokenized/binary_slice.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
l = [ 1 , 2 , 3 , 4 , 5 , 6 ] <EOL>
|
||||
print ( l [ 1 : 3 ] ) <EOL>
|
||||
print ( l [ : 2 ] ) <EOL>
|
||||
print ( l [ 3 : ] ) <EOL>
|
||||
print ( l [ - 4 : ] ) <EOL>
|
||||
print ( l [ : - 2 ] ) <EOL>
|
||||
print ( l [ : ] ) <EOL>
|
7
tests/tokenized/store_slice.txt
Normal file
7
tests/tokenized/store_slice.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
a = [ 0 ] * 16 <EOL>
|
||||
l = [ 1 , 2 , 3 ] <EOL>
|
||||
a [ 13 : ] = l <EOL>
|
||||
a [ 5 ] = 10 <EOL>
|
||||
a [ : 2 ] = [ 1 , 2 ] <EOL>
|
||||
a [ : ] = range ( 16 ) <EOL>
|
||||
print ( a ) <EOL>
|
46
tests/tokenized/test_loops3.txt
Normal file
46
tests/tokenized/test_loops3.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
def loop1 ( ) : <EOL>
|
||||
<INDENT>
|
||||
iterable = [ 1 , 2 , 3 ] <EOL>
|
||||
for item in iterable : <EOL>
|
||||
<INDENT>
|
||||
pass <EOL>
|
||||
<OUTDENT>
|
||||
<OUTDENT>
|
||||
loop1 ( ) <EOL>
|
||||
def loop2 ( ) : <EOL>
|
||||
<INDENT>
|
||||
for i in range ( 2 ) : <EOL>
|
||||
<INDENT>
|
||||
print ( i ) <EOL>
|
||||
<OUTDENT>
|
||||
<OUTDENT>
|
||||
loop2 ( ) <EOL>
|
||||
def loop3 ( ) : <EOL>
|
||||
<INDENT>
|
||||
def loop ( ) : <EOL>
|
||||
<INDENT>
|
||||
x = ( 1 , 2 , 3 ) <EOL>
|
||||
l = [ ] <EOL>
|
||||
for i in x : <EOL>
|
||||
<INDENT>
|
||||
l . append ( i ) <EOL>
|
||||
<OUTDENT>
|
||||
return l <EOL>
|
||||
<OUTDENT>
|
||||
return loop ( ) <EOL>
|
||||
<OUTDENT>
|
||||
loop3 ( ) <EOL>
|
||||
def loop4 ( ) : <EOL>
|
||||
<INDENT>
|
||||
for i in range ( 3 ) : <EOL>
|
||||
<INDENT>
|
||||
for j in range ( 2 ) : <EOL>
|
||||
<INDENT>
|
||||
print ( i * j ) <EOL>
|
||||
<OUTDENT>
|
||||
<OUTDENT>
|
||||
<OUTDENT>
|
||||
loop4 ( ) <EOL>
|
||||
for j in [ 1 , 2 , 3 ] [ : : - 1 ] : <EOL>
|
||||
<INDENT>
|
||||
print ( 'hi' , j ) <EOL>
|
9
tests/tokenized/test_raise_varargs.txt
Normal file
9
tests/tokenized/test_raise_varargs.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
import struct <EOL>
|
||||
def bytes_to_words ( b ) : <EOL>
|
||||
<INDENT>
|
||||
'Convert a byte string (little-endian) to a list of 32-bit words.' <EOL>
|
||||
if len ( b ) % 4 != 0 : <EOL>
|
||||
<INDENT>
|
||||
raise ValueError ( 'Input bytes length must be a multiple of 4 for word conversion.' ) <EOL>
|
||||
<OUTDENT>
|
||||
return struct . unpack ( '<' + 'I' * ( len ( b ) // 4 ) , b ) <EOL>
|
Reference in New Issue
Block a user