Compare commits

16 Commits

Author SHA1 Message Date
Michael Hansen
a267bfb47f Merge pull request #561 from whoami730/bug-fix
Fix bug in RAISE_VARARGS
2025-07-04 12:41:02 -07:00
Sahil Jain
8b0ea9450e Fix RAISE_VARARGS bug 2025-07-04 19:16:04 +05:30
Michael Hansen
7d2039d24e Merge pull request #557 from whoami730/new-opcodes
Support py 3.12 opcodes: part 2
2025-07-02 07:58:23 -07:00
Michael Hansen
e64ea4bdec Merge branch 'master' into new-opcodes 2025-07-02 07:56:28 -07:00
Michael Hansen
4badfa6321 Merge pull request #555 from whoami730/master
Support py 3.12 opcodes: part 1
2025-07-02 07:51:36 -07:00
Sahil Jain
6e0089e01c Update 2025-07-01 22:52:59 +05:30
Sahil Jain
3afcfbc6a7 Fix compilation error 2025-07-01 14:01:05 +05:30
Sahil Jain
aa292c7682 Add newlines 2025-07-01 09:27:07 +05:30
Sahil Jain
5fe61462a2 Support COPY opcde 2025-07-01 09:25:49 +05:30
Sahil Jain
ad5f39db56 Support SLICE opcodes 2025-07-01 09:24:53 +05:30
Sahil Jain
97ec04789d Add JUMP_BACKWARD + CACHE comments 2025-07-01 09:22:57 +05:30
Sahil Jain
6dae4e801f Remove COPY opcode 2025-07-01 09:22:57 +05:30
Sahil Jain
040732920b Add comment 2025-07-01 09:22:57 +05:30
Sahil Jain
a93fd14672 Add new loop tests 2025-07-01 09:22:57 +05:30
Sahil Jain
a4a6a24f3e Support END_FOR opcode 2025-07-01 09:22:56 +05:30
Michael Hansen
307e0b8fd7 Update to upload-artifact/v4 to fix CI 2025-06-30 19:21:38 -07:00
17 changed files with 260 additions and 9 deletions

View File

@@ -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
View File

@@ -6,3 +6,5 @@
/.kdev4
__pycache__
tests-out
build/
.vscode/

View File

@@ -897,7 +897,10 @@ PycRef<ASTNode> BuildFromCode(PycRef<PycCode> code, PycModule* mod)
case Pyc::INSTRUMENTED_FOR_ITER_A:
{
PycRef<ASTNode> iter = stack.top(); // Iterable
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 +1166,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)
@@ -1678,10 +1684,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);
@@ -1787,8 +1820,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 +2504,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;

View File

@@ -30,15 +30,31 @@ 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
{

View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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[:])

View 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)

View 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)

View 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)

View 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>

View 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>

View 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>

View 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>