Compare commits

...

23 Commits

Author SHA1 Message Date
5e70e1e084 fix: 3.14 magic is 3627 (0xE2B)
Some checks failed
Build / build (macos-latest) (push) Has been cancelled
Build / build (ubuntu-latest) (push) Has been cancelled
Build / windows-build (push) Has been cancelled
2026-01-18 19:14:32 +08:00
1c413c5124 ci: workflow_dispatch 2025-11-19 00:10:29 +08:00
49557e7580 fix(pycdc): don't skip 1 instruction right after RETURN_VALUE 2025-11-19 00:08:21 +08:00
6c6aa159df sync with 71c3307: unit buffer 2025-11-19 00:07:46 +08:00
b5983f505f sync with 766647f: prompt, only 1 file, flush on err 2025-11-18 23:55:37 +08:00
7a0044914c Merge py3.14 das, commit '1d123722' 2025-11-18 23:50:39 +08:00
3359b0e1b2 fix: 3.13 is supported 2025-09-18 21:49:31 +08:00
8b8fe3020a Merge branch 'master' into fork 2025-09-18 16:35:01 +08:00
Michael Hansen
a05ddec0d8 Merge pull request #564 from whoami730/exceptions
Some checks failed
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Linux-CI / build (push) Has been cancelled
MSVC-CI / build (push) Has been cancelled
Parse and display exception table
2025-08-30 10:02:14 -07:00
Sahil Jain
d8c6fdf711 Address comments 2025-08-30 20:01:32 +05:30
Michael Hansen
577720302e Add basic protection aginst circular references in pycdas and pycdc.
This fixes the last case of fuzzer errors detected by #572.
2025-08-28 16:42:03 -07:00
Michael Hansen
38799f5cfb Also check EOF in getBuffer() 2025-08-28 15:58:28 -07:00
Michael Hansen
0e7be40367 Add some extra guards against null dereference and empty std::stack pops
Fixes segfault cases of #572
2025-08-28 15:36:35 -07:00
Michael Hansen
ff0c1450b4 Abort immediately when attempting to read past end of stream.
No consumers of readByte() were actually checking for EOF, so they would
all keep re-reading the same byte over and over again, potentially until the
process runs out of memory (ref #572).
2025-08-28 15:14:55 -07:00
Alex
1d1237228a Add missed python 3.14 cpp module 2025-07-27 14:46:12 +03:00
Alex
97e49d644f Initial support for Python 3.14 2025-07-27 14:29:01 +03:00
db5f282e61 Merge branch 'master' into fork 2025-07-03 12:25:12 +08:00
299a59f0a9 ci: build workflow 2025-03-17 20:36:42 +08:00
45ea5f4ac8 fix: flush das before dc 2025-03-14 22:25:48 +08:00
33f2cc57c0 chore: remove codeql 2025-03-11 13:26:41 +08:00
e4f96df166 fix: avoid popping defblock and segfault 2025-03-07 15:36:03 +08:00
93c9e47e82 sync with d9e00c3 2025-03-07 11:59:55 +08:00
a2b8ab1205 sync with dc5d2b9 2025-03-05 22:55:46 +08:00
17 changed files with 871 additions and 216 deletions

46
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Build
on:
push:
branches: [fork]
pull_request:
workflow_dispatch:
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Build
run: |
mkdir build
cd build
cmake ..
cmake --build . --config Debug
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: pycdc-fork-${{ matrix.os }}
path: build/pycdc
windows-build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
mkdir build
cd build
cmake -G "MinGW Makefiles" ..
cmake --build . --config Debug
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: pycdc-fork-windows
path: build/pycdc.exe

View File

@@ -1,67 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 1 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['cpp', 'python']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- if: matrix.language == 'python'
name: Autobuild Python
uses: github/codeql-action/autobuild@v2
- if: matrix.language == 'cpp'
name: Build C++
run: |
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,29 +0,0 @@
name: Linux-CI
on:
push:
branches: [master]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Configure and Build
run: |
(
mkdir build-debug && cd build-debug
cmake -DCMAKE_BUILD_TYPE=Debug ..
make -j4
)
(
mkdir build-release && cd build-release
cmake -DCMAKE_BUILD_TYPE=Debug ..
make -j4
)
- name: Test
run: |
cmake --build build-debug --target check
cmake --build build-release --target check

View File

@@ -1,29 +0,0 @@
name: MSVC-CI
on:
push:
branches: [master]
pull_request:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Configure and Build
run: |
mkdir build
cd build
cmake -G "Visual Studio 17 2022" -A Win32 ..
cmake --build . --config Debug
cmake --build . --config Release
- name: Test
run: |
cmake --build build --config Debug --target check
cmake --build build --config Release --target check
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: pycdc-release
path: build\Release\*.exe

View File

@@ -1,6 +1,7 @@
#include <cstring> #include <cstring>
#include <cstdint> #include <cstdint>
#include <stdexcept> #include <stdexcept>
#include <unordered_set>
#include "ASTree.h" #include "ASTree.h"
#include "FastStack.h" #include "FastStack.h"
#include "pyc_numeric.h" #include "pyc_numeric.h"
@@ -423,6 +424,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} }
break; break;
case Pyc::CALL_A: case Pyc::CALL_A:
// BEGIN ONESHOT TEMPORARY PATCH
case Pyc::CALL_KW_A:
// END ONESHOT PATCH
case Pyc::CALL_FUNCTION_A: case Pyc::CALL_FUNCTION_A:
case Pyc::INSTRUMENTED_CALL_A: case Pyc::INSTRUMENTED_CALL_A:
{ {
@@ -454,6 +458,17 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack.pop(); stack.pop();
PycRef<ASTNode> loadbuild = stack.top(); PycRef<ASTNode> loadbuild = stack.top();
stack.pop(); stack.pop();
// BEGIN ONESHOT TEMPORARY PATCH
if (loadbuild == nullptr)
{
loadbuild = stack.top();
stack.pop();
}
if (stack.top() == nullptr)
{
stack.pop();
}
// END ONESHOT PATCH
int loadbuild_type = loadbuild.type(); int loadbuild_type = loadbuild.type();
if (loadbuild_type == ASTNode::NODE_LOADBUILDCLASS) { if (loadbuild_type == ASTNode::NODE_LOADBUILDCLASS) {
PycRef<ASTNode> call = new ASTCall(function, pparamList, kwparamList); PycRef<ASTNode> call = new ASTCall(function, pparamList, kwparamList);
@@ -467,13 +482,19 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack_hist.pop(); stack_hist.pop();
} }
/* // BEGIN ONESHOT TEMPORARY PATCH
KW_NAMES(i) if (mod->verCompare(3, 13) >= 0 && opcode == Pyc::CALL_KW_A) {
Stores a reference to co_consts[consti] into an internal variable for use by CALL. PycRef<PycTuple> kw_names = stack.top().cast<ASTObject>()->object().cast<PycTuple>();
co_consts[consti] must be a tuple of strings. stack.pop();
New in version 3.11. kwparams = kw_names->values().size();
*/ pparams = operand - kwparams;
if (mod->verCompare(3, 11) >= 0) { for (auto it = kw_names->values().rbegin(); it != kw_names->values().rend(); it++) {
kwparamList.push_front(std::make_pair(new ASTObject(*it), stack.top()));
stack.pop();
}
}
else if (mod->verCompare(3, 11) >= 0) {
// END ONESHOT PATCH
PycRef<ASTNode> object_or_map = stack.top(); PycRef<ASTNode> object_or_map = stack.top();
if (object_or_map.type() == ASTNode::NODE_KW_NAMES_MAP) { if (object_or_map.type() == ASTNode::NODE_KW_NAMES_MAP) {
stack.pop(); stack.pop();
@@ -513,12 +534,22 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
pparamList.push_front(param); pparamList.push_front(param);
} }
} }
// BEGIN ONESHOT TEMPORARY PATCH
// For Python 3.13 and later
if (mod->verCompare(3, 13) >= 0) {
PycRef<ASTNode> self_or_null = stack.top();
stack.pop();
if (self_or_null != nullptr) {
pparamList.push_front(self_or_null);
}
}
PycRef<ASTNode> func = stack.top(); PycRef<ASTNode> func = stack.top();
stack.pop(); stack.pop();
if ((opcode == Pyc::CALL_A || opcode == Pyc::INSTRUMENTED_CALL_A) && if ((opcode == Pyc::CALL_A || opcode == Pyc::INSTRUMENTED_CALL_A) &&
stack.top() == nullptr) { mod->verCompare(3, 13) < 0 && stack.top() == nullptr) {
stack.pop(); stack.pop();
} }
// END ONESHOT PATCH
stack.push(new ASTCall(func, pparamList, kwparamList)); stack.push(new ASTCall(func, pparamList, kwparamList));
} }
@@ -607,6 +638,41 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack.push(call); stack.push(call);
} }
break; break;
// BEGIN ONESHOT TEMPORARY PATCH
case Pyc::CALL_FUNCTION_EX_A:
{
PycRef<ASTNode> kw;
if (operand & 0x01)
{
kw = stack.top();
stack.pop();
}
PycRef<ASTNode> var = stack.top();
stack.pop();
ASTCall::pparam_t pparamList;
if (mod->verCompare(3, 13) >= 0)
{
PycRef<ASTNode> param = stack.top();
stack.pop();
if (param != nullptr)
pparamList.push_front(param);
}
PycRef<ASTNode> func = stack.top();
stack.pop();
if (mod->verCompare(3, 13) < 0 && stack.top() == nullptr)
{
stack.pop();
}
PycRef<ASTNode> call = new ASTCall(func, pparamList, ASTCall::kwparam_t());
if (operand & 0x01)
call.cast<ASTCall>()->setKW(kw);
call.cast<ASTCall>()->setVar(var);
stack.push(call);
}
break;
// END ONESHOT PATCH
case Pyc::CALL_METHOD_A: case Pyc::CALL_METHOD_A:
{ {
ASTCall::pparam_t pparamList; ASTCall::pparam_t pparamList;
@@ -1231,8 +1297,12 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
break; break;
} }
stack = stack_hist.top(); if (!stack_hist.empty()) {
stack_hist.pop(); 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> prev = curblock;
PycRef<ASTBlock> nil; PycRef<ASTBlock> nil;
@@ -1313,6 +1383,19 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
bool push = true; bool push = true;
do { do {
// BEGIN ONESHOT TEMPORARY PATCH
// This implementation is probably wrong
// Skip junk code on top level scope
auto &top = blocks.top();
if (top == defblock) {
pos += offs;
for (int i = 0; i < offs; i++) {
source.getByte();
}
break;
}
// END ONESHOT PATCH
blocks.pop(); blocks.pop();
if (!blocks.empty()) if (!blocks.empty())
@@ -1380,7 +1463,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
blocks.push(except); blocks.push(except);
} }
} else { } else {
fprintf(stderr, "Something TERRIBLE happened!!\n"); fprintf(stderr, "Something TERRIBLE happened!! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
} }
prev = nil; prev = nil;
} else { } else {
@@ -1389,10 +1473,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} while (prev != nil); } while (prev != nil);
curblock = blocks.top(); if (!blocks.empty()) {
curblock = blocks.top();
if (curblock->blktype() == ASTBlock::BLK_EXCEPT) { if (curblock->blktype() == ASTBlock::BLK_EXCEPT)
curblock->setEnd(pos+offs); curblock->setEnd(pos+offs);
} }
} }
break; break;
@@ -1556,6 +1640,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
break; break;
case Pyc::MAKE_CLOSURE_A: case Pyc::MAKE_CLOSURE_A:
case Pyc::MAKE_FUNCTION_A: case Pyc::MAKE_FUNCTION_A:
// BEGIN ONESHOT TEMPORARY PATCH
case Pyc::MAKE_FUNCTION:
// END ONESHOT PATCH
{ {
PycRef<ASTNode> fun_code = stack.top(); PycRef<ASTNode> fun_code = stack.top();
stack.pop(); stack.pop();
@@ -1569,19 +1656,49 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} }
ASTFunction::defarg_t defArgs, kwDefArgs; ASTFunction::defarg_t defArgs, kwDefArgs;
const int defCount = operand & 0xFF; // BEGIN ONESHOT TEMPORARY PATCH
const int kwDefCount = (operand >> 8) & 0xFF; if (mod->verCompare(3, 6) < 0)
for (int i = 0; i < defCount; ++i) { {
defArgs.push_front(stack.top()); const int defCount = operand & 0xFF;
stack.pop(); const int kwDefCount = (operand >> 8) & 0xFF;
for (int i = 0; i < defCount; ++i) {
defArgs.push_front(stack.top());
stack.pop();
}
for (int i = 0; i < kwDefCount; ++i) {
kwDefArgs.push_front(stack.top());
stack.pop();
}
if ((operand >> 16) & 0x7FFF) {
// a tuple listing the parameter names for the annotations (only if there are any annotation objects)
stack.pop();
}
} }
for (int i = 0; i < kwDefCount; ++i) { else if (mod->verCompare(3, 13) < 0)
kwDefArgs.push_front(stack.top()); {
stack.pop(); if (operand & 0x08)
stack.pop();
if (operand & 0x04)
stack.pop();
if (operand & 0x02)
stack.pop();
if (operand & 0x01)
stack.pop();
} }
// END ONESHOT PATCH
stack.push(new ASTFunction(fun_code, defArgs, kwDefArgs)); stack.push(new ASTFunction(fun_code, defArgs, kwDefArgs));
} }
break; break;
// BEGIN ONESHOT TEMPORARY PATCH
case Pyc::SET_FUNCTION_ATTRIBUTE_A:
{
PycRef<ASTNode> func = stack.top();
stack.pop();
stack.pop();
stack.push(func);
}
break;
// END ONESHOT PATCH
case Pyc::NOP: case Pyc::NOP:
break; break;
case Pyc::POP_BLOCK: case Pyc::POP_BLOCK:
@@ -1612,9 +1729,17 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack = stack_hist.top(); stack = stack_hist.top();
stack_hist.pop(); stack_hist.pop();
} else { } else {
fprintf(stderr, "Warning: Stack history is empty, something wrong might have happened\n"); fprintf(stderr, "Warning: Stack history is empty, something wrong might have happened at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
} }
} }
// BEGIN ONESHOT TEMPORARY PATCH
// The problem is not here, but in the way the blocks are created
// Add this to avoid segfault
if (blocks.top() == defblock)
break;
// END ONESHOT PATCH
PycRef<ASTBlock> tmp = curblock; PycRef<ASTBlock> tmp = curblock;
blocks.pop(); blocks.pop();
@@ -1769,7 +1894,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
else else
curblock->append(new ASTPrint(stack.top(), stream)); curblock->append(new ASTPrint(stack.top(), stream));
stack.pop(); stack.pop();
stream->setProcessed(); if (stream)
stream->setProcessed();
} }
break; break;
case Pyc::PRINT_NEWLINE: case Pyc::PRINT_NEWLINE:
@@ -1797,7 +1923,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
else else
curblock->append(new ASTPrint(nullptr, stream)); curblock->append(new ASTPrint(nullptr, stream));
stack.pop(); stack.pop();
stream->setProcessed(); if (stream)
stream->setProcessed();
} }
break; break;
case Pyc::RAISE_VARARGS_A: case Pyc::RAISE_VARARGS_A:
@@ -1842,7 +1969,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
curblock = blocks.top(); curblock = blocks.top();
curblock->append(prev.cast<ASTNode>()); curblock->append(prev.cast<ASTNode>());
bc_next(source, mod, opcode, operand, pos); // BEGIN ONESHOT TEMPORARY PATCH
// bc_next(source, mod, opcode, operand, pos);
// END ONESHOT PATCH
} }
} }
break; break;
@@ -1921,7 +2050,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack.pop(); stack.pop();
if (none != NULL) { if (none != NULL) {
fprintf(stderr, "Something TERRIBLE happened!\n"); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
break; break;
} }
@@ -2029,7 +2159,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(attr); tup.cast<ASTTuple>()->add(attr);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2064,7 +2195,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(name); tup.cast<ASTTuple>()->add(name);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2104,7 +2236,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(name); tup.cast<ASTTuple>()->add(name);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2163,7 +2296,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(name); tup.cast<ASTTuple>()->add(name);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2205,7 +2339,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(name); tup.cast<ASTTuple>()->add(name);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2326,7 +2461,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
if (tup.type() == ASTNode::NODE_TUPLE) if (tup.type() == ASTNode::NODE_TUPLE)
tup.cast<ASTTuple>()->add(save); tup.cast<ASTTuple>()->add(save);
else else
fputs("Something TERRIBLE happened!\n", stderr); fprintf(stderr, "Something TERRIBLE happened! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
if (--unpack <= 0) { if (--unpack <= 0) {
stack.pop(); stack.pop();
@@ -2481,6 +2617,9 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
as no-ops. */ as no-ops. */
break; break;
case Pyc::PUSH_NULL: case Pyc::PUSH_NULL:
// BEGIN ONESHOT TEMPORARY PATCH
case Pyc::BEGIN_FINALLY:
// END ONESHOT PATCH
stack.push(nullptr); stack.push(nullptr);
break; break;
case Pyc::GEN_START_A: case Pyc::GEN_START_A:
@@ -2504,6 +2643,34 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
stack.push(next_tup); stack.push(next_tup);
} }
break; break;
// BEGIN ONESHOT TEMPORARY PATCH
// THESE OPCODES ARE NOT IMPLEMENTED HERE
case Pyc::PUSH_EXC_INFO:
{
stack.push(stack.top());
}
break;
case Pyc::JUMP_IF_NOT_EXC_MATCH_A:
{
PycRef<ASTNode> ex_type = stack.top();
stack.pop();
PycRef<ASTNode> cur_ex = stack.top();
stack.pop();
}
break;
case Pyc::RERAISE:
case Pyc::RERAISE_A:
case Pyc::MAKE_CELL_A:
case Pyc::COPY_FREE_VARS_A:
case Pyc::TO_BOOL:
case Pyc::CALL_INTRINSIC_1_A:
break;
case Pyc::CALL_INTRINSIC_2_A:
{
stack.pop();
}
break;
// END ONESHOT PATCH
case Pyc::BINARY_SLICE: case Pyc::BINARY_SLICE:
{ {
PycRef<ASTNode> end = stack.top(); PycRef<ASTNode> end = stack.top();
@@ -2578,7 +2745,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} }
break; break;
default: default:
fprintf(stderr, "Unsupported opcode: %s (%d)\n", Pyc::OpcodeName(opcode), opcode); fprintf(stderr, "Unsupported opcode: %s (%d) at %s\n",
Pyc::OpcodeName(opcode),
opcode,
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
cleanBuild = false; cleanBuild = false;
return new ASTNodeList(defblock->nodes()); return new ASTNodeList(defblock->nodes());
} }
@@ -2590,7 +2760,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} }
if (stack_hist.size()) { if (stack_hist.size()) {
fputs("Warning: Stack history is not empty!\n", stderr); fprintf(stderr, "Warning: Stack history is not empty! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
while (stack_hist.size()) { while (stack_hist.size()) {
stack_hist.pop(); stack_hist.pop();
@@ -2598,7 +2769,8 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
} }
if (blocks.size() > 1) { if (blocks.size() > 1) {
fputs("Warning: block stack is not empty!\n", stderr); fprintf(stderr, "Warning: block stack is not empty! at %s\n",
mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value());
while (blocks.size() > 1) { while (blocks.size() > 1) {
PycRef<ASTBlock> tmp = blocks.top(); PycRef<ASTBlock> tmp = blocks.top();
@@ -2773,6 +2945,8 @@ void print_formatted_value(PycRef<ASTFormattedValue> formatted_value, PycModule*
pyc_output << "}"; pyc_output << "}";
} }
static std::unordered_set<ASTNode *> node_seen;
void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output) void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
{ {
if (node == NULL) { if (node == NULL) {
@@ -2781,6 +2955,12 @@ void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
return; 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()) { switch (node->type()) {
case ASTNode::NODE_BINARY: case ASTNode::NODE_BINARY:
case ASTNode::NODE_COMPARE: case ASTNode::NODE_COMPARE:
@@ -3002,6 +3182,12 @@ void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
case ASTNode::NODE_BLOCK: case ASTNode::NODE_BLOCK:
{ {
PycRef<ASTBlock> blk = node.cast<ASTBlock>(); PycRef<ASTBlock> blk = node.cast<ASTBlock>();
// BEGIN ONESHOT TEMPORARY PATCH
if (blk->blktype() == ASTBlock::BLK_MAIN)
break;
// END ONESHOT PATCH
if (blk->blktype() == ASTBlock::BLK_ELSE && blk->size() == 0) if (blk->blktype() == ASTBlock::BLK_ELSE && blk->size() == 0)
break; break;
@@ -3436,10 +3622,12 @@ void print_src(PycRef<ASTNode> node, PycModule* mod, std::ostream& pyc_output)
pyc_output << "<NODE:" << node->type() << ">"; pyc_output << "<NODE:" << node->type() << ">";
fprintf(stderr, "Unsupported Node type: %d\n", node->type()); fprintf(stderr, "Unsupported Node type: %d\n", node->type());
cleanBuild = false; cleanBuild = false;
node_seen.erase((ASTNode *)node);
return; return;
} }
cleanBuild = true; cleanBuild = true;
node_seen.erase((ASTNode *)node);
} }
bool print_docstring(PycRef<PycObject> obj, int indent, PycModule* mod, bool print_docstring(PycRef<PycObject> obj, int indent, PycModule* mod,
@@ -3456,8 +3644,16 @@ bool print_docstring(PycRef<PycObject> obj, int indent, PycModule* mod,
return false; return false;
} }
static std::unordered_set<PycCode *> code_seen;
void decompyle(PycRef<PycCode> code, PycModule* mod, std::ostream& pyc_output) 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<ASTNode> source = BuildFromCode(code, mod);
PycRef<ASTNodeList> clean = source.cast<ASTNodeList>(); PycRef<ASTNodeList> clean = source.cast<ASTNodeList>();
@@ -3551,4 +3747,6 @@ void decompyle(PycRef<PycCode> code, PycModule* mod, std::ostream& pyc_output)
start_line(cur_indent, pyc_output); start_line(cur_indent, pyc_output);
pyc_output << "# WARNING: Decompyle incomplete\n"; pyc_output << "# WARNING: Decompyle incomplete\n";
} }
code_seen.erase((PycCode *)code);
} }

View File

@@ -23,6 +23,10 @@ elseif(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif() endif()
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++ ${CMAKE_EXE_LINKER_FLAGS}")
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_library(pycxx STATIC add_library(pycxx STATIC
@@ -62,15 +66,10 @@ add_library(pycxx STATIC
bytes/python_3_11.cpp bytes/python_3_11.cpp
bytes/python_3_12.cpp bytes/python_3_12.cpp
bytes/python_3_13.cpp bytes/python_3_13.cpp
bytes/python_3_14.cpp
) )
add_executable(pycdas pycdas.cpp) add_executable(pycdc pycdas.cpp ASTree.cpp ASTNode.cpp)
target_link_libraries(pycdas pycxx)
install(TARGETS pycdas
RUNTIME DESTINATION bin)
add_executable(pycdc pycdc.cpp ASTree.cpp ASTNode.cpp)
target_link_libraries(pycdc pycxx) target_link_libraries(pycdc pycxx)
install(TARGETS pycdc install(TARGETS pycdc

View File

@@ -39,6 +39,7 @@ DECLARE_PYTHON(3, 10)
DECLARE_PYTHON(3, 11) DECLARE_PYTHON(3, 11)
DECLARE_PYTHON(3, 12) DECLARE_PYTHON(3, 12)
DECLARE_PYTHON(3, 13) DECLARE_PYTHON(3, 13)
DECLARE_PYTHON(3, 14)
const char* Pyc::OpcodeName(int opcode) const char* Pyc::OpcodeName(int opcode)
{ {
@@ -109,6 +110,7 @@ int Pyc::ByteToOpcode(int maj, int min, int opcode)
case 11: return python_3_11_map(opcode); case 11: return python_3_11_map(opcode);
case 12: return python_3_12_map(opcode); case 12: return python_3_12_map(opcode);
case 13: return python_3_13_map(opcode); case 13: return python_3_13_map(opcode);
case 14: return python_3_14_map(opcode);
} }
break; break;
} }
@@ -604,14 +606,14 @@ void bc_disasm(std::ostream& pyc_output, PycRef<PycCode> code, PycModule* mod,
void bc_exceptiontable(std::ostream& pyc_output, PycRef<PycCode> code, void bc_exceptiontable(std::ostream& pyc_output, PycRef<PycCode> code,
int indent) int indent)
{ {
for (auto tuple: code->exceptTableEntries()) { for (const auto& entry : code->exceptionTableEntries()) {
for (int i=0; i<indent; i++) for (int i=0; i<indent; i++)
pyc_output << " "; pyc_output << " ";
pyc_output << std::get<0>(tuple) << " to " << std::get<1>(tuple); pyc_output << entry.start_offset << " to " << entry.end_offset
pyc_output << " -> " << std::get<2>(tuple) << " "; << " -> " << entry.target << " [" << entry.stack_depth
pyc_output << "[" << std::get<3>(tuple) << "] " << (std::get<4>(tuple) ? "lasti": ""); << "] " << (entry.push_lasti ? "lasti": "")
pyc_output << "\n"; << "\n";
} }
} }

View File

@@ -124,6 +124,104 @@ OPCODE(FORMAT_WITH_SPEC) // Python 3.13 ->
OPCODE(MAKE_FUNCTION) // Python 3.13 -> OPCODE(MAKE_FUNCTION) // Python 3.13 ->
OPCODE(TO_BOOL) // Python 3.13 -> OPCODE(TO_BOOL) // Python 3.13 ->
OPCODE(BUILD_TEMPLATE) // Python 3.14 ->
OPCODE(BINARY_OP_INPLACE_ADD_UNICODE)
OPCODE(NOT_TAKEN)
OPCODE(POP_ITER)
OPCODE(BUILD_INTERPOLATION)
OPCODE(LOAD_COMMON_CONSTANT)
OPCODE(LOAD_FAST_BORROW)
OPCODE(LOAD_FAST_BORROW_LOAD_FAST_BORROW)
OPCODE(LOAD_SPECIAL)
OPCODE(BINARY_OP_ADD_FLOAT)
OPCODE(BINARY_OP_ADD_INT)
OPCODE(BINARY_OP_ADD_UNICODE)
OPCODE(BINARY_OP_EXTEND)
OPCODE(BINARY_OP_MULTIPLY_FLOAT)
OPCODE(BINARY_OP_MULTIPLY_INT)
OPCODE(BINARY_OP_SUBSCR_DICT)
OPCODE(BINARY_OP_SUBSCR_GETITEM)
OPCODE(BINARY_OP_SUBSCR_LIST_INT)
OPCODE(BINARY_OP_SUBSCR_LIST_SLICE)
OPCODE(BINARY_OP_SUBSCR_STR_INT)
OPCODE(BINARY_OP_SUBSCR_TUPLE_INT)
OPCODE(BINARY_OP_SUBTRACT_FLOAT)
OPCODE(BINARY_OP_SUBTRACT_INT)
OPCODE(CALL_ALLOC_AND_ENTER_INIT)
OPCODE(CALL_BOUND_METHOD_EXACT_ARGS)
OPCODE(CALL_BOUND_METHOD_GENERAL)
OPCODE(CALL_BUILTIN_CLASS)
OPCODE(CALL_BUILTIN_FAST)
OPCODE(CALL_BUILTIN_FAST_WITH_KEYWORDS)
OPCODE(CALL_BUILTIN_O)
OPCODE(CALL_ISINSTANCE)
OPCODE(CALL_KW_BOUND_METHOD)
OPCODE(CALL_KW_NON_PY)
OPCODE(CALL_KW_PY)
OPCODE(CALL_LEN)
OPCODE(CALL_LIST_APPEND)
OPCODE(CALL_METHOD_DESCRIPTOR_FAST)
OPCODE(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS)
OPCODE(CALL_METHOD_DESCRIPTOR_NOARGS)
OPCODE(CALL_METHOD_DESCRIPTOR_O)
OPCODE(CALL_NON_PY_GENERAL)
OPCODE(CALL_PY_EXACT_ARGS)
OPCODE(CALL_PY_GENERAL)
OPCODE(CALL_STR_1)
OPCODE(CALL_TUPLE_1)
OPCODE(CALL_TYPE_1)
OPCODE(COMPARE_OP_FLOAT)
OPCODE(COMPARE_OP_INT)
OPCODE(COMPARE_OP_STR)
OPCODE(CONTAINS_OP_DICT)
OPCODE(CONTAINS_OP_SET)
OPCODE(FOR_ITER_GEN)
OPCODE(FOR_ITER_LIST)
OPCODE(FOR_ITER_RANGE)
OPCODE(FOR_ITER_TUPLE)
OPCODE(JUMP_BACKWARD_JIT)
OPCODE(JUMP_BACKWARD_NO_JIT)
OPCODE(LOAD_ATTR_CLASS)
OPCODE(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK)
OPCODE(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN)
OPCODE(LOAD_ATTR_INSTANCE_VALUE)
OPCODE(LOAD_ATTR_METHOD_LAZY_DICT)
OPCODE(LOAD_ATTR_METHOD_NO_DICT)
OPCODE(LOAD_ATTR_METHOD_WITH_VALUES)
OPCODE(LOAD_ATTR_MODULE)
OPCODE(LOAD_ATTR_NONDESCRIPTOR_NO_DICT)
OPCODE(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES)
OPCODE(LOAD_ATTR_PROPERTY)
OPCODE(LOAD_ATTR_SLOT)
OPCODE(LOAD_ATTR_WITH_HINT)
OPCODE(LOAD_CONST_IMMORTAL)
OPCODE(LOAD_CONST_MORTAL)
OPCODE(LOAD_GLOBAL_BUILTIN)
OPCODE(LOAD_GLOBAL_MODULE)
OPCODE(LOAD_SUPER_ATTR_ATTR)
OPCODE(LOAD_SUPER_ATTR_METHOD)
OPCODE(RESUME_CHECK)
OPCODE(SEND_GEN)
OPCODE(STORE_ATTR_INSTANCE_VALUE)
OPCODE(STORE_ATTR_SLOT)
OPCODE(STORE_ATTR_WITH_HINT)
OPCODE(STORE_SUBSCR_DICT)
OPCODE(STORE_SUBSCR_LIST_INT)
OPCODE(TO_BOOL_ALWAYS_TRUE)
OPCODE(TO_BOOL_BOOL)
OPCODE(TO_BOOL_INT)
OPCODE(TO_BOOL_LIST)
OPCODE(TO_BOOL_NONE)
OPCODE(TO_BOOL_STR)
OPCODE(UNPACK_SEQUENCE_LIST)
OPCODE(UNPACK_SEQUENCE_TUPLE)
OPCODE(UNPACK_SEQUENCE_TWO_TUPLE)
OPCODE(ANNOTATIONS_PLACEHOLDER)
OPCODE(JUMP)
OPCODE(JUMP_NO_INTERRUPT)
OPCODE(SETUP_CLEANUP)
OPCODE(STORE_FAST_MAYBE_NULL)
/* Has parameter word */ /* Has parameter word */
OPCODE_A_FIRST(STORE_NAME) // Python 1.0 -> names[A] OPCODE_A_FIRST(STORE_NAME) // Python 1.0 -> names[A]
OPCODE_A(DELETE_NAME) // Python 1.0 -> names[A] OPCODE_A(DELETE_NAME) // Python 1.0 -> names[A]
@@ -269,6 +367,7 @@ OPCODE_A(LOAD_FAST_LOAD_FAST) // Python 3.13 -> A=locals
OPCODE_A(SET_FUNCTION_ATTRIBUTE) // Python 3.13 -> A=attribute_type OPCODE_A(SET_FUNCTION_ATTRIBUTE) // Python 3.13 -> A=attribute_type
OPCODE_A(STORE_FAST_LOAD_FAST) // Python 3.13 -> A=locals[A<<4]+locals[A&0xf] OPCODE_A(STORE_FAST_LOAD_FAST) // Python 3.13 -> A=locals[A<<4]+locals[A&0xf]
OPCODE_A(STORE_FAST_STORE_FAST) // Python 3.13 -> A=locals[A<<4]+locals[A&0xf] OPCODE_A(STORE_FAST_STORE_FAST) // Python 3.13 -> A=locals[A<<4]+locals[A&0xf]
OPCODE_A(LOAD_SMALL_INT) // Python 3.14 -> A=small int range(256)
/* Instrumented opcodes */ /* Instrumented opcodes */
OPCODE_A(INSTRUMENTED_LOAD_SUPER_ATTR) // Python 3.12 -> (see LOAD_SUPER_ATTR) OPCODE_A(INSTRUMENTED_LOAD_SUPER_ATTR) // Python 3.12 -> (see LOAD_SUPER_ATTR)
@@ -290,3 +389,6 @@ OPCODE_A(INSTRUMENTED_END_SEND) // Python 3.12 -> (see END
OPCODE_A(INSTRUMENTED_INSTRUCTION) // Python 3.12 -> A=(unused) OPCODE_A(INSTRUMENTED_INSTRUCTION) // Python 3.12 -> A=(unused)
OPCODE_A(INSTRUMENTED_LINE) // Python 3.12 -> ??? OPCODE_A(INSTRUMENTED_LINE) // Python 3.12 -> ???
OPCODE_A(INSTRUMENTED_CALL_KW) // Python 3.13 -> (see CALL_KW) OPCODE_A(INSTRUMENTED_CALL_KW) // Python 3.13 -> (see CALL_KW)
OPCODE_A(INSTRUMENTED_POP_ITER) // Python 3.14 -> (see POP_ITER)
OPCODE_A(INSTRUMENTED_NOT_TAKEN) // Python 3.14 -> (see NOT_TAKEN)
OPCODE_A(INSTRUMENTED_END_ASYNC_FOR) // Python 3.14 -> (see END_ASYNC_FOR)

242
bytes/python_3_14.cpp Normal file
View File

@@ -0,0 +1,242 @@
#include "bytecode_map.h"
BEGIN_MAP(3, 14)
MAP_OP(0, CACHE)
MAP_OP(1, BINARY_SLICE)
MAP_OP(2, BUILD_TEMPLATE)
MAP_OP(3, BINARY_OP_INPLACE_ADD_UNICODE)
MAP_OP(4, CALL_FUNCTION_EX_A)
MAP_OP(5, CHECK_EG_MATCH)
MAP_OP(6, CHECK_EXC_MATCH)
MAP_OP(7, CLEANUP_THROW)
MAP_OP(8, DELETE_SUBSCR)
MAP_OP(9, END_FOR)
MAP_OP(10, END_SEND)
MAP_OP(11, EXIT_INIT_CHECK)
MAP_OP(12, FORMAT_SIMPLE)
MAP_OP(13, FORMAT_WITH_SPEC)
MAP_OP(14, GET_AITER)
MAP_OP(15, GET_ANEXT)
MAP_OP(16, GET_ITER)
MAP_OP(17, RESERVED)
MAP_OP(18, GET_LEN)
MAP_OP(19, GET_YIELD_FROM_ITER)
MAP_OP(20, INTERPRETER_EXIT)
MAP_OP(21, LOAD_BUILD_CLASS)
MAP_OP(22, LOAD_LOCALS)
MAP_OP(23, MAKE_FUNCTION)
MAP_OP(24, MATCH_KEYS)
MAP_OP(25, MATCH_MAPPING)
MAP_OP(26, MATCH_SEQUENCE)
MAP_OP(27, NOP)
MAP_OP(28, NOT_TAKEN)
MAP_OP(29, POP_EXCEPT)
MAP_OP(30, POP_ITER)
MAP_OP(31, POP_TOP)
MAP_OP(32, PUSH_EXC_INFO)
MAP_OP(33, PUSH_NULL)
MAP_OP(34, RETURN_GENERATOR)
MAP_OP(35, RETURN_VALUE)
MAP_OP(36, SETUP_ANNOTATIONS)
MAP_OP(37, STORE_SLICE)
MAP_OP(38, STORE_SUBSCR)
MAP_OP(39, TO_BOOL)
MAP_OP(40, UNARY_INVERT)
MAP_OP(41, UNARY_NEGATIVE)
MAP_OP(42, UNARY_NOT)
MAP_OP(43, WITH_EXCEPT_START)
MAP_OP(44, BINARY_OP_A)
MAP_OP(45, BUILD_INTERPOLATION)
MAP_OP(46, BUILD_LIST_A)
MAP_OP(47, BUILD_MAP_A)
MAP_OP(48, BUILD_SET_A)
MAP_OP(49, BUILD_SLICE_A)
MAP_OP(50, BUILD_STRING_A)
MAP_OP(51, BUILD_TUPLE_A)
MAP_OP(52, CALL_A)
MAP_OP(53, CALL_INTRINSIC_1_A)
MAP_OP(54, CALL_INTRINSIC_2_A)
MAP_OP(55, CALL_KW_A)
MAP_OP(56, COMPARE_OP_A)
MAP_OP(57, CONTAINS_OP_A)
MAP_OP(58, CONVERT_VALUE_A)
MAP_OP(59, COPY_A)
MAP_OP(60, COPY_FREE_VARS_A)
MAP_OP(61, DELETE_ATTR_A)
MAP_OP(62, DELETE_DEREF_A)
MAP_OP(63, DELETE_FAST_A)
MAP_OP(64, DELETE_GLOBAL_A)
MAP_OP(65, DELETE_NAME_A)
MAP_OP(66, DICT_MERGE_A)
MAP_OP(67, DICT_UPDATE_A)
MAP_OP(68, END_ASYNC_FOR)
MAP_OP(69, EXTENDED_ARG_A)
MAP_OP(70, FOR_ITER_A)
MAP_OP(71, GET_AWAITABLE_A)
MAP_OP(72, IMPORT_FROM_A)
MAP_OP(73, IMPORT_NAME_A)
MAP_OP(74, IS_OP_A)
MAP_OP(75, JUMP_BACKWARD_A)
MAP_OP(76, JUMP_BACKWARD_NO_INTERRUPT_A)
MAP_OP(77, JUMP_FORWARD_A)
MAP_OP(78, LIST_APPEND)
MAP_OP(79, LIST_EXTEND_A)
MAP_OP(80, LOAD_ATTR_A)
MAP_OP(81, LOAD_COMMON_CONSTANT)
MAP_OP(82, LOAD_CONST_A)
MAP_OP(83, LOAD_DEREF_A)
MAP_OP(84, LOAD_FAST_A)
MAP_OP(85, LOAD_FAST_AND_CLEAR_A)
MAP_OP(86, LOAD_FAST_BORROW)
MAP_OP(87, LOAD_FAST_BORROW_LOAD_FAST_BORROW)
MAP_OP(88, LOAD_FAST_CHECK_A)
MAP_OP(89, LOAD_FAST_LOAD_FAST_A)
MAP_OP(90, LOAD_FROM_DICT_OR_DEREF_A)
MAP_OP(91, LOAD_FROM_DICT_OR_GLOBALS_A)
MAP_OP(92, LOAD_GLOBAL_A)
MAP_OP(93, LOAD_NAME_A)
MAP_OP(94, LOAD_SMALL_INT_A)
MAP_OP(95, LOAD_SPECIAL)
MAP_OP(96, LOAD_SUPER_ATTR_A)
MAP_OP(97, MAKE_CELL_A)
MAP_OP(98, MAP_ADD_A)
MAP_OP(99, MATCH_CLASS_A)
MAP_OP(100, POP_JUMP_IF_FALSE_A)
MAP_OP(101, POP_JUMP_IF_NONE_A)
MAP_OP(102, POP_JUMP_IF_NOT_NONE_A)
MAP_OP(103, POP_JUMP_IF_TRUE_A)
MAP_OP(104, RAISE_VARARGS_A)
MAP_OP(105, RERAISE)
MAP_OP(106, SEND_A)
MAP_OP(107, SET_ADD)
MAP_OP(108, SET_FUNCTION_ATTRIBUTE_A)
MAP_OP(109, SET_UPDATE_A)
MAP_OP(110, STORE_ATTR_A)
MAP_OP(111, STORE_DEREF_A)
MAP_OP(112, STORE_FAST_A)
MAP_OP(113, STORE_FAST_LOAD_FAST_A)
MAP_OP(114, STORE_FAST_STORE_FAST_A)
MAP_OP(115, STORE_GLOBAL_A)
MAP_OP(116, STORE_NAME_A)
MAP_OP(117, SWAP_A)
MAP_OP(118, UNPACK_EX_A)
MAP_OP(119, UNPACK_SEQUENCE_A)
MAP_OP(120, YIELD_VALUE_A)
MAP_OP(128, RESUME_A)
MAP_OP(129, BINARY_OP_ADD_FLOAT)
MAP_OP(130, BINARY_OP_ADD_INT)
MAP_OP(131, BINARY_OP_ADD_UNICODE)
MAP_OP(132, BINARY_OP_EXTEND)
MAP_OP(133, BINARY_OP_MULTIPLY_FLOAT)
MAP_OP(134, BINARY_OP_MULTIPLY_INT)
MAP_OP(135, BINARY_OP_SUBSCR_DICT)
MAP_OP(136, BINARY_OP_SUBSCR_GETITEM)
MAP_OP(137, BINARY_OP_SUBSCR_LIST_INT)
MAP_OP(138, BINARY_OP_SUBSCR_LIST_SLICE)
MAP_OP(139, BINARY_OP_SUBSCR_STR_INT)
MAP_OP(140, BINARY_OP_SUBSCR_TUPLE_INT)
MAP_OP(141, BINARY_OP_SUBTRACT_FLOAT)
MAP_OP(142, BINARY_OP_SUBTRACT_INT)
MAP_OP(143, CALL_ALLOC_AND_ENTER_INIT)
MAP_OP(144, CALL_BOUND_METHOD_EXACT_ARGS)
MAP_OP(145, CALL_BOUND_METHOD_GENERAL)
MAP_OP(146, CALL_BUILTIN_CLASS)
MAP_OP(147, CALL_BUILTIN_FAST)
MAP_OP(148, CALL_BUILTIN_FAST_WITH_KEYWORDS)
MAP_OP(149, CALL_BUILTIN_O)
MAP_OP(150, CALL_ISINSTANCE)
MAP_OP(151, CALL_KW_BOUND_METHOD)
MAP_OP(152, CALL_KW_NON_PY)
MAP_OP(153, CALL_KW_PY)
MAP_OP(154, CALL_LEN)
MAP_OP(155, CALL_LIST_APPEND)
MAP_OP(156, CALL_METHOD_DESCRIPTOR_FAST)
MAP_OP(157, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS)
MAP_OP(158, CALL_METHOD_DESCRIPTOR_NOARGS)
MAP_OP(159, CALL_METHOD_DESCRIPTOR_O)
MAP_OP(160, CALL_NON_PY_GENERAL)
MAP_OP(161, CALL_PY_EXACT_ARGS)
MAP_OP(162, CALL_PY_GENERAL)
MAP_OP(163, CALL_STR_1)
MAP_OP(164, CALL_TUPLE_1)
MAP_OP(165, CALL_TYPE_1)
MAP_OP(166, COMPARE_OP_FLOAT)
MAP_OP(167, COMPARE_OP_INT)
MAP_OP(168, COMPARE_OP_STR)
MAP_OP(169, CONTAINS_OP_DICT)
MAP_OP(170, CONTAINS_OP_SET)
MAP_OP(171, FOR_ITER_GEN)
MAP_OP(172, FOR_ITER_LIST)
MAP_OP(173, FOR_ITER_RANGE)
MAP_OP(174, FOR_ITER_TUPLE)
MAP_OP(175, JUMP_BACKWARD_JIT)
MAP_OP(176, JUMP_BACKWARD_NO_JIT)
MAP_OP(177, LOAD_ATTR_CLASS)
MAP_OP(178, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK)
MAP_OP(179, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN)
MAP_OP(180, LOAD_ATTR_INSTANCE_VALUE)
MAP_OP(181, LOAD_ATTR_METHOD_LAZY_DICT)
MAP_OP(182, LOAD_ATTR_METHOD_NO_DICT)
MAP_OP(183, LOAD_ATTR_METHOD_WITH_VALUES)
MAP_OP(184, LOAD_ATTR_MODULE)
MAP_OP(185, LOAD_ATTR_NONDESCRIPTOR_NO_DICT)
MAP_OP(186, LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES)
MAP_OP(187, LOAD_ATTR_PROPERTY)
MAP_OP(188, LOAD_ATTR_SLOT)
MAP_OP(189, LOAD_ATTR_WITH_HINT)
MAP_OP(190, LOAD_CONST_IMMORTAL)
MAP_OP(191, LOAD_CONST_MORTAL)
MAP_OP(192, LOAD_GLOBAL_BUILTIN)
MAP_OP(193, LOAD_GLOBAL_MODULE)
MAP_OP(194, LOAD_SUPER_ATTR_ATTR)
MAP_OP(195, LOAD_SUPER_ATTR_METHOD)
MAP_OP(196, RESUME_CHECK)
MAP_OP(197, SEND_GEN)
MAP_OP(198, STORE_ATTR_INSTANCE_VALUE)
MAP_OP(199, STORE_ATTR_SLOT)
MAP_OP(200, STORE_ATTR_WITH_HINT)
MAP_OP(201, STORE_SUBSCR_DICT)
MAP_OP(202, STORE_SUBSCR_LIST_INT)
MAP_OP(203, TO_BOOL_ALWAYS_TRUE)
MAP_OP(204, TO_BOOL_BOOL)
MAP_OP(205, TO_BOOL_INT)
MAP_OP(206, TO_BOOL_LIST)
MAP_OP(207, TO_BOOL_NONE)
MAP_OP(208, TO_BOOL_STR)
MAP_OP(209, UNPACK_SEQUENCE_LIST)
MAP_OP(210, UNPACK_SEQUENCE_TUPLE)
MAP_OP(211, UNPACK_SEQUENCE_TWO_TUPLE)
MAP_OP(234, INSTRUMENTED_END_FOR_A)
MAP_OP(235, INSTRUMENTED_POP_ITER_A)
MAP_OP(236, INSTRUMENTED_END_SEND_A)
MAP_OP(237, INSTRUMENTED_FOR_ITER_A)
MAP_OP(238, INSTRUMENTED_INSTRUCTION_A)
MAP_OP(239, INSTRUMENTED_JUMP_FORWARD_A)
MAP_OP(240, INSTRUMENTED_NOT_TAKEN_A)
MAP_OP(241, INSTRUMENTED_POP_JUMP_IF_TRUE_A)
MAP_OP(242, INSTRUMENTED_POP_JUMP_IF_FALSE_A)
MAP_OP(243, INSTRUMENTED_POP_JUMP_IF_NONE_A)
MAP_OP(244, INSTRUMENTED_POP_JUMP_IF_NOT_NONE_A)
MAP_OP(245, INSTRUMENTED_RESUME_A)
MAP_OP(246, INSTRUMENTED_RETURN_VALUE_A)
MAP_OP(247, INSTRUMENTED_YIELD_VALUE_A)
MAP_OP(248, INSTRUMENTED_END_ASYNC_FOR_A)
MAP_OP(249, INSTRUMENTED_LOAD_SUPER_ATTR_A)
MAP_OP(250, INSTRUMENTED_CALL_A)
MAP_OP(251, INSTRUMENTED_CALL_KW_A)
MAP_OP(252, INSTRUMENTED_CALL_FUNCTION_EX_A)
MAP_OP(253, INSTRUMENTED_JUMP_BACKWARD_A)
MAP_OP(254, INSTRUMENTED_LINE_A)
MAP_OP(255, ENTER_EXECUTOR_A)
MAP_OP(256, ANNOTATIONS_PLACEHOLDER)
MAP_OP(257, JUMP)
MAP_OP(258, JUMP_IF_FALSE_A)
MAP_OP(259, JUMP_IF_TRUE_A)
MAP_OP(260, JUMP_NO_INTERRUPT)
MAP_OP(261, LOAD_CLOSURE_A)
MAP_OP(262, POP_BLOCK)
MAP_OP(263, SETUP_CLEANUP)
MAP_OP(264, SETUP_FINALLY_A)
MAP_OP(265, SETUP_WITH_A)
MAP_OP(266, STORE_FAST_MAYBE_NULL)
END_MAP()

View File

@@ -53,35 +53,43 @@ bool PycFile::atEof() const
int PycFile::getByte() int PycFile::getByte()
{ {
int ch = fgetc(m_stream); int ch = fgetc(m_stream);
if (ch == EOF) if (ch == EOF) {
ungetc(ch, m_stream); fputs("PycFile::getByte(): Unexpected end of stream\n", stderr);
std::exit(1);
}
return ch; 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 */ /* PycBuffer */
int PycBuffer::getByte() int PycBuffer::getByte()
{ {
if (atEof()) if (atEof()) {
return EOF; fputs("PycBuffer::getByte(): Unexpected end of stream\n", stderr);
std::exit(1);
}
int ch = (int)(*(m_buffer + m_pos)); int ch = (int)(*(m_buffer + m_pos));
++m_pos; ++m_pos;
return ch & 0xFF; // Make sure it's just a byte! 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) if (m_pos + bytes > m_size) {
bytes = m_size - m_pos; fputs("PycBuffer::getBuffer(): Unexpected end of stream\n", stderr);
std::exit(1);
}
if (bytes != 0) if (bytes != 0)
memcpy(buffer, (m_buffer + m_pos), bytes); memcpy(buffer, (m_buffer + m_pos), bytes);
m_pos += bytes; m_pos += bytes;
return bytes;
} }
int formatted_print(std::ostream& stream, const char* format, ...) int formatted_print(std::ostream& stream, const char* format, ...)

6
data.h
View File

@@ -19,7 +19,7 @@ public:
virtual bool atEof() const = 0; virtual bool atEof() const = 0;
virtual int getByte() = 0; virtual int getByte() = 0;
virtual int getBuffer(int bytes, void* buffer) = 0; virtual void getBuffer(int bytes, void* buffer) = 0;
int get16(); int get16();
int get32(); int get32();
Pyc_INT64 get64(); Pyc_INT64 get64();
@@ -34,7 +34,7 @@ public:
bool atEof() const override; bool atEof() const override;
int getByte() override; int getByte() override;
int getBuffer(int bytes, void* buffer) override; void getBuffer(int bytes, void* buffer) override;
private: private:
FILE* m_stream; FILE* m_stream;
@@ -50,7 +50,7 @@ public:
bool atEof() const override { return (m_pos == m_size); } bool atEof() const override { return (m_pos == m_size); }
int getByte() override; int getByte() override;
int getBuffer(int bytes, void* buffer) override; void getBuffer(int bytes, void* buffer) override;
private: private:
const unsigned char* m_buffer; const unsigned char* m_buffer;

View File

@@ -67,8 +67,8 @@ void PycCode::load(PycData* stream, PycModule* mod)
if (mod->verCompare(3, 8) < 0) { if (mod->verCompare(3, 8) < 0) {
// Remap flags to new values introduced in 3.8 // Remap flags to new values introduced in 3.8
if (m_flags & 0xF0000000) if (m_flags & 0xF0000000)
throw std::runtime_error("Cannot remap unexpected flags"); fprintf(stderr, "Remapping flags (%08X) may not be correct\n", m_flags);
m_flags = (m_flags & 0xFFFF) | ((m_flags & 0xFFF0000) << 4); m_flags = (m_flags & 0x1FFF) | ((m_flags & 0xFFFE000) << 4);
} }
m_code = LoadObject(stream, mod).cast<PycString>(); m_code = LoadObject(stream, mod).cast<PycString>();
@@ -133,23 +133,23 @@ int _parse_varint(PycBuffer& data, int& pos) {
int b = data.getByte(); int b = data.getByte();
pos += 1; pos += 1;
int val = b & 63; int val = b & 0x3F;
while (b & 64) { while (b & 0x40) {
val <<= 6; val <<= 6;
b = data.getByte(); b = data.getByte();
pos += 1; pos += 1;
val |= (b & 63); val |= (b & 0x3F);
} }
return val; return val;
} }
std::vector<PycCode::exception_table_entry_t> PycCode::exceptTableEntries() const std::vector<PycExceptionTableEntry> PycCode::exceptionTableEntries() const
{ {
PycBuffer data(m_exceptTable->value(), m_exceptTable->length()); PycBuffer data(m_exceptTable->value(), m_exceptTable->length());
std::vector<exception_table_entry_t> entries; std::vector<PycExceptionTableEntry> entries;
int pos = 0; int pos = 0;
while (!data.atEof()) { while (!data.atEof()) {
@@ -164,8 +164,8 @@ std::vector<PycCode::exception_table_entry_t> PycCode::exceptTableEntries() cons
int depth = dl >> 1; int depth = dl >> 1;
bool lasti = bool(dl & 1); bool lasti = bool(dl & 1);
entries.emplace_back(start, end, target, depth, lasti); entries.push_back(PycExceptionTableEntry(start, end, target, depth, lasti));
} }
return entries; return entries;
} }

View File

@@ -8,6 +8,18 @@
class PycData; class PycData;
class PycModule; 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 { class PycCode : public PycObject {
public: public:
typedef std::vector<PycRef<PycString>> globals_t; typedef std::vector<PycRef<PycString>> globals_t;
@@ -87,9 +99,7 @@ public:
m_globalsUsed.emplace_back(std::move(varname)); m_globalsUsed.emplace_back(std::move(varname));
} }
typedef std::tuple<int, int, int, int, bool> exception_table_entry_t; std::vector<PycExceptionTableEntry> exceptionTableEntries() const;
std::vector<exception_table_entry_t> exceptTableEntries() const;
private: private:
int m_argCount, m_posOnlyArgCount, m_kwOnlyArgCount, m_numLocals; int m_argCount, m_posOnlyArgCount, m_kwOnlyArgCount, m_numLocals;

View File

@@ -182,6 +182,12 @@ void PycModule::setVersion(unsigned int magic)
m_unicode = true; m_unicode = true;
break; break;
case MAGIC_3_14:
m_maj = 3;
m_min = 14;
m_unicode = true;
break;
/* Bad Magic detected */ /* Bad Magic detected */
default: default:
m_maj = -1; m_maj = -1;
@@ -197,7 +203,7 @@ bool PycModule::isSupportedVersion(int major, int minor)
case 2: case 2:
return (minor >= 0 && minor <= 7); return (minor >= 0 && minor <= 7);
case 3: case 3:
return (minor >= 0 && minor <= 12); return (minor >= 0 && minor <= 14);
default: default:
return false; return false;
} }

View File

@@ -36,6 +36,7 @@ enum PycMagic {
MAGIC_3_11 = 0x0A0D0DA7, MAGIC_3_11 = 0x0A0D0DA7,
MAGIC_3_12 = 0x0A0D0DCB, MAGIC_3_12 = 0x0A0D0DCB,
MAGIC_3_13 = 0x0A0D0DF3, MAGIC_3_13 = 0x0A0D0DF3,
MAGIC_3_14 = 0x0A0D0E2B,
INVALID = 0, INVALID = 0,
}; };

View File

@@ -1,12 +1,30 @@
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <type_traits>
#endif
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <cstdarg> #include <cstdarg>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <unordered_set>
#include "pyc_module.h" #include "pyc_module.h"
#include "pyc_numeric.h" #include "pyc_numeric.h"
#include "bytecode.h" #include "bytecode.h"
#include "ASTree.h"
#ifdef WIN32 #ifdef WIN32
# define PATHSEP '\\' # define PATHSEP '\\'
@@ -14,6 +32,68 @@
# define PATHSEP '/' # define PATHSEP '/'
#endif #endif
#ifdef _WIN32
// Windows: Use SEH/UEF; prefer calling only Win32 APIs
#ifdef __cpp_lib_fstream_native_handle
static HANDLE g_dc_h = INVALID_HANDLE_VALUE;
static HANDLE g_das_h = INVALID_HANDLE_VALUE;
#endif
static LONG WINAPI av_handler(EXCEPTION_POINTERS* /*ep*/) {
const char msg[] = "Access violation caught. Best-effort FlushFileBuffers.\n";
DWORD wrote = 0;
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, sizeof(msg) - 1, &wrote, nullptr);
#ifdef __cpp_lib_fstream_native_handle
if (g_das_h != INVALID_HANDLE_VALUE) FlushFileBuffers(g_das_h);
if (g_dc_h != INVALID_HANDLE_VALUE) FlushFileBuffers(g_dc_h);
#endif
TerminateProcess(GetCurrentProcess(), 0xC0000005);
return EXCEPTION_EXECUTE_HANDLER;
}
struct SehInstall {
SehInstall() {
// Suppress WER popups; let the UEF handle it directly
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
SetUnhandledExceptionFilter(av_handler);
}
} seh_install_guard;
#else // !_WIN32
#ifdef __cpp_lib_fstream_native_handle
static int g_dc_fd = -1;
static int g_das_fd = -1;
static void segv_handler(int sig) {
const char msg[] = "Access violation caught. Best-effort fsync.\n";
// Only use async-signal-safe functions
write(STDERR_FILENO, msg, sizeof(msg)-1);
if (g_das_fd != -1) fsync(g_das_fd);
if (g_dc_fd != -1) fsync(g_dc_fd);
_Exit(128 + sig);
}
#else
static void segv_handler(int sig) {
const char msg[] = "Access violation caught.\n";
write(STDERR_FILENO, msg, sizeof(msg)-1);
_Exit(128 + sig);
}
#endif
struct SegvInstall {
SegvInstall() {
struct sigaction sa{};
sa.sa_handler = segv_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGSEGV, &sa, nullptr);
}
} segv_install_guard;
#endif // _WIN32
static const char* flag_names[] = { static const char* flag_names[] = {
"CO_OPTIMIZED", "CO_NEWLOCALS", "CO_VARARGS", "CO_VARKEYWORDS", "CO_OPTIMIZED", "CO_NEWLOCALS", "CO_VARARGS", "CO_VARKEYWORDS",
"CO_NESTED", "CO_GENERATOR", "CO_NOFREE", "CO_COROUTINE", "CO_NESTED", "CO_GENERATOR", "CO_NOFREE", "CO_COROUTINE",
@@ -73,6 +153,8 @@ static void iprintf(std::ostream& pyc_output, int indent, const char* fmt, ...)
va_end(varargs); va_end(varargs);
} }
static std::unordered_set<PycObject *> out_seen;
void output_object(PycRef<PycObject> obj, PycModule* mod, int indent, void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
unsigned flags, std::ostream& pyc_output) unsigned flags, std::ostream& pyc_output)
{ {
@@ -81,6 +163,12 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
return; 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()) { switch (obj->type()) {
case PycObject::TYPE_CODE: case PycObject::TYPE_CODE:
case PycObject::TYPE_CODE2: case PycObject::TYPE_CODE2:
@@ -104,7 +192,7 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
unsigned int orig_flags = codeObj->flags(); unsigned int orig_flags = codeObj->flags();
if (mod->verCompare(3, 8) < 0) { if (mod->verCompare(3, 8) < 0) {
// Remap flags back to the value stored in the PyCode object // Remap flags back to the value stored in the PyCode object
orig_flags = (orig_flags & 0xFFFF) | ((orig_flags & 0xFFF00000) >> 4); orig_flags = (orig_flags & 0x1FFF) | ((orig_flags & 0xFFFE0000) >> 4);
} }
iprintf(pyc_output, indent + 1, "Flags: 0x%08X", orig_flags); iprintf(pyc_output, indent + 1, "Flags: 0x%08X", orig_flags);
print_coflags(codeObj->flags(), pyc_output); print_coflags(codeObj->flags(), pyc_output);
@@ -246,28 +334,27 @@ void output_object(PycRef<PycObject> obj, PycModule* mod, int indent,
default: default:
iprintf(pyc_output, indent, "<TYPE: %d>\n", obj->type()); iprintf(pyc_output, indent, "<TYPE: %d>\n", obj->type());
} }
out_seen.erase((PycObject *)obj);
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
fputs("LilRan's fork of Pycdc, output will be written to <filename>.das and <filename>.cdc.py\n", stderr);
const char* infile = nullptr; const char* infile = nullptr;
const char* outfile = nullptr;
bool marshalled = false; bool marshalled = false;
const char* version = nullptr; const char* version = nullptr;
unsigned disasm_flags = 0; unsigned disasm_flags = 0;
std::ostream* pyc_output = &std::cout; bool unitbuf = false;
std::ofstream out_file; std::ofstream dc_out_file;
std::ofstream das_out_file;
for (int arg = 1; arg < argc; ++arg) { for (int arg = 1; arg < argc; ++arg) {
if (strcmp(argv[arg], "-o") == 0) { if (strcmp(argv[arg], "-o") == 0) {
if (arg + 1 < argc) { if (arg + 1 < argc) {
const char* filename = argv[++arg]; outfile = argv[++arg];
out_file.open(filename, std::ios_base::out);
if (out_file.fail()) {
fprintf(stderr, "Error opening file '%s' for writing\n",
filename);
return 1;
}
pyc_output = &out_file;
} else { } else {
fputs("Option '-o' requires a filename\n", stderr); fputs("Option '-o' requires a filename\n", stderr);
return 1; return 1;
@@ -288,18 +375,25 @@ int main(int argc, char* argv[])
} else if (strcmp(argv[arg], "--help") == 0 || strcmp(argv[arg], "-h") == 0) { } else if (strcmp(argv[arg], "--help") == 0 || strcmp(argv[arg], "-h") == 0) {
fprintf(stderr, "Usage: %s [options] input.pyc\n\n", argv[0]); fprintf(stderr, "Usage: %s [options] input.pyc\n\n", argv[0]);
fputs("Options:\n", stderr); fputs("Options:\n", stderr);
fputs(" -o <filename> Write output to <filename> (default: stdout)\n", stderr); fputs(" -o <filename> Write output to <filename>.das and <filename>.cdc.py\n", stderr);
fputs(" -c Specify loading a compiled code object. Requires the version to be set\n", stderr); fputs(" -c Specify loading a compiled code object. Requires the version to be set\n", stderr);
fputs(" -v <x.y> Specify a Python version for loading a compiled code object\n", stderr); fputs(" -v <x.y> Specify a Python version for loading a compiled code object\n", stderr);
fputs(" --pycode-extra Show extra fields in PyCode object dumps\n", stderr); fputs(" --pycode-extra Show extra fields in PyCode object dumps\n", stderr);
fputs(" --show-caches Don't suprress CACHE instructions in Python 3.11+ disassembly\n", stderr); fputs(" --show-caches Don't suprress CACHE instructions in Python 3.11+ disassembly\n", stderr);
fputs(" --unitbuf Set output streams to be unbuffered\n", stderr);
fputs(" --help Show this help text and then exit\n", stderr); fputs(" --help Show this help text and then exit\n", stderr);
return 0; return 0;
} else if (strcmp(argv[arg], "--unitbuf") == 0) {
unitbuf = true;
} else if (argv[arg][0] == '-') { } else if (argv[arg][0] == '-') {
fprintf(stderr, "Error: Unrecognized argument %s\n", argv[arg]); fprintf(stderr, "Error: Unrecognized argument %s\n", argv[arg]);
return 1; return 1;
} else { } else if (!infile) {
infile = argv[arg]; infile = argv[arg];
} else {
fprintf(stderr, "Error: Only one input file allowed, got %s and %s\n",
infile, argv[arg]);
return 1;
} }
} }
@@ -308,6 +402,54 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
std::string prefix_name(outfile ? outfile : infile);
dc_out_file.open(prefix_name + ".cdc.py", std::ios_base::out);
if (unitbuf) {
dc_out_file.setf(std::ios::unitbuf);
}
if (dc_out_file.fail()) {
fprintf(stderr, "Error opening file '%s' for writing\n", (prefix_name + ".cdc.py").c_str());
return 1;
}
das_out_file.open(prefix_name + ".das", std::ios_base::out);
if (unitbuf) {
das_out_file.setf(std::ios::unitbuf);
}
if (das_out_file.fail()) {
fprintf(stderr, "Error opening file '%s' for writing\n", (prefix_name + ".das").c_str());
return 1;
}
#ifdef __cpp_lib_fstream_native_handle
#ifndef _WIN32
g_dc_fd = dc_out_file.native_handle();
g_das_fd = das_out_file.native_handle();
#else
// Extract underlying handles to flush on exceptions
// MSVC's native_handle is typically a HANDLE; MinGW may return a fd, requiring conversion via _get_osfhandle
auto dc_nh = dc_out_file.native_handle();
auto das_nh = das_out_file.native_handle();
using native_handle_t = decltype(dc_nh);
if constexpr (std::is_same_v<native_handle_t, HANDLE>) {
g_dc_h = dc_nh;
g_das_h = das_nh;
} else if constexpr (std::is_integral_v<native_handle_t>) {
intptr_t dc_handle = _get_osfhandle(dc_nh);
if (dc_handle != -1 && dc_handle != reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE)) {
g_dc_h = reinterpret_cast<HANDLE>(dc_handle);
}
intptr_t das_handle = _get_osfhandle(das_nh);
if (das_handle != -1 && das_handle != reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE)) {
g_das_h = reinterpret_cast<HANDLE>(das_handle);
}
} else {
// ignore, keep as INVALID_HANDLE_VALUE
}
#endif
#endif
PycModule mod; PycModule mod;
if (!marshalled) { if (!marshalled) {
try { try {
@@ -333,16 +475,38 @@ int main(int argc, char* argv[])
} }
const char* dispname = strrchr(infile, PATHSEP); const char* dispname = strrchr(infile, PATHSEP);
dispname = (dispname == NULL) ? infile : dispname + 1; dispname = (dispname == NULL) ? infile : dispname + 1;
formatted_print(*pyc_output, "%s (Python %d.%d%s)\n", dispname,
formatted_print(das_out_file, "%s (Python %d.%d%s)\n", dispname,
mod.majorVer(), mod.minorVer(), mod.majorVer(), mod.minorVer(),
(mod.majorVer() < 3 && mod.isUnicode()) ? " -U" : ""); (mod.majorVer() < 3 && mod.isUnicode()) ? " -U" : "");
try { try {
output_object(mod.code().try_cast<PycObject>(), &mod, 0, disasm_flags, output_object(mod.code().try_cast<PycObject>(), &mod, 0, disasm_flags,
*pyc_output); das_out_file);
} catch (std::exception& ex) { } catch (std::exception& ex) {
das_out_file.flush();
das_out_file.close();
fprintf(stderr, "Error disassembling %s: %s\n", infile, ex.what()); fprintf(stderr, "Error disassembling %s: %s\n", infile, ex.what());
return 1; return 1;
} }
das_out_file.flush();
das_out_file.close();
dc_out_file << "# Source Generated with Decompyle++\n";
formatted_print(dc_out_file, "# File: %s (Python %d.%d%s)\n\n", dispname,
mod.majorVer(), mod.minorVer(),
(mod.majorVer() < 3 && mod.isUnicode()) ? " Unicode" : "");
try {
decompyle(mod.code(), &mod, dc_out_file);
} catch (std::exception& ex) {
dc_out_file.flush();
dc_out_file.close();
fprintf(stderr, "Error decompyling %s: %s\n", infile, ex.what());
return 1;
}
dc_out_file.flush();
dc_out_file.close();
return 0; return 0;
} }

View File

@@ -1,3 +1,5 @@
// THIS FILE IS UNUSED FOR ONESHOT FORK
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>