Skip to content
This repository has been archived by the owner on Jul 17, 2024. It is now read-only.

feat: Add support for Python 3.12 #27

Merged
merged 6 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ jobs:
distribution: 'temurin'
cache: 'maven'
# Need to install all Python versions in the same run for tox
- name: Python 3.9, Python 3.10, Python 3.11 Setup
- name: Python 3.10, Python 3.11, Python 3.12 Setup
uses: actions/setup-python@v4
with:
python-version: |
3.9
3.10
3.11
3.12
cache: 'pip'
cache-dependency-path: |
**/setup.py
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ jobs:
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
# Need to install all Python versions in the same run for tox
- name: Python 3.9, Python 3.10, Python 3.11 Setup
- name: Python 3.10, Python 3.11, Python 3.12 Setup
uses: actions/setup-python@v4
with:
python-version: |
3.9
3.10
3.11
3.12
cache: 'pip'
cache-dependency-path: |
**/setup.py
Expand Down
4 changes: 2 additions & 2 deletions jpyinterpreter/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ def run(self):
'org-stubs': 'src/main/resources',
},
test_suite='tests',
python_requires='>=3.9',
python_requires='>=3.10',
install_requires=[
'JPype1>=1.4.1',
'JPype1>=1.5.0',
],
cmdclass={'build_py': FetchDependencies},
package_data={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package ai.timefold.jpyinterpreter;

import java.util.Objects;

public enum CompareOp {
LESS_THAN(0, "__lt__"),
LESS_THAN_OR_EQUALS(1, "__le__"),
EQUALS(2, "__eq__"),
NOT_EQUALS(3, "__ne__"),
GREATER_THAN(4, "__gt__"),
GREATER_THAN_OR_EQUALS(5, "__ge__");
LESS_THAN("<", "__lt__"),
LESS_THAN_OR_EQUALS("<=", "__le__"),
EQUALS("==", "__eq__"),
NOT_EQUALS("!=", "__ne__"),
GREATER_THAN(">", "__gt__"),
GREATER_THAN_OR_EQUALS(">=", "__ge__");

public final int id;
public final String id;
public final String dunderMethod;

CompareOp(int id, String dunderMethod) {
CompareOp(String id, String dunderMethod) {
this.id = id;
this.dunderMethod = dunderMethod;
}
Expand All @@ -25,9 +27,9 @@ public static CompareOp getOpForDunderMethod(String dunderMethod) {
throw new IllegalArgumentException("No Op corresponds to dunder method (" + dunderMethod + ")");
}

public static CompareOp getOp(int id) {
public static CompareOp getOp(String id) {
triceo marked this conversation as resolved.
Show resolved Hide resolved
for (CompareOp op : CompareOp.values()) {
if (op.id == id) {
if (Objects.equals(op.id, id)) {
return op;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,36 @@

import ai.timefold.jpyinterpreter.opcodes.descriptor.OpcodeDescriptor;

public record PythonBytecodeInstruction(String opname, int offset, int arg, OptionalInt startsLine,
public record PythonBytecodeInstruction(String opname, int offset, int arg,
String argRepr, OptionalInt startsLine,
boolean isJumpTarget) {
public static PythonBytecodeInstruction atOffset(String opname, int offset) {
return new PythonBytecodeInstruction(opname, offset, 0, OptionalInt.empty(), false);
return new PythonBytecodeInstruction(opname, offset, 0, "", OptionalInt.empty(), false);
triceo marked this conversation as resolved.
Show resolved Hide resolved
}

public static PythonBytecodeInstruction atOffset(OpcodeDescriptor instruction, int offset) {
return atOffset(instruction.name(), offset);
}

public PythonBytecodeInstruction withArg(int newArg) {
return new PythonBytecodeInstruction(opname, offset, newArg, startsLine, isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, newArg, argRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction withArgRepr(String newArgRepr) {
return new PythonBytecodeInstruction(opname, offset, arg, newArgRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction startsLine(int lineNumber) {
return new PythonBytecodeInstruction(opname, offset, arg, OptionalInt.of(lineNumber), isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, OptionalInt.of(lineNumber),
isJumpTarget);
}

public PythonBytecodeInstruction withIsJumpTarget(boolean isJumpTarget) {
return new PythonBytecodeInstruction(opname, offset, arg, startsLine, isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction markAsJumpTarget() {
return new PythonBytecodeInstruction(opname, offset, arg, startsLine, true);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, startsLine, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
Expand All @@ -30,6 +31,7 @@
import ai.timefold.jpyinterpreter.opcodes.OpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.SelfOpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.descriptor.GeneratorOpDescriptor;
import ai.timefold.jpyinterpreter.opcodes.variable.LoadFastAndClearOpcode;
import ai.timefold.jpyinterpreter.types.BuiltinTypes;
import ai.timefold.jpyinterpreter.types.PythonLikeFunction;
import ai.timefold.jpyinterpreter.types.PythonLikeType;
Expand Down Expand Up @@ -252,7 +254,7 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
null);

translatePythonBytecodeToMethod(methodDescriptor, internalClassName, methodVisitor, pythonCompiledFunction,
isPythonLikeFunction, Integer.MAX_VALUE, isVirtual); // TODO: Use actual python version
isPythonLikeFunction, isVirtual);

classWriter.visitEnd();

Expand Down Expand Up @@ -296,7 +298,7 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
null);

translatePythonBytecodeToMethod(methodDescriptor, internalClassName, methodVisitor, pythonCompiledFunction,
isPythonLikeFunction, Integer.MAX_VALUE, isVirtual); // TODO: Use actual python version
isPythonLikeFunction, isVirtual);

String withoutGenericsSignature = Type.getMethodDescriptor(methodWithoutGenerics);
if (!withoutGenericsSignature.equals(methodDescriptor.getMethodDescriptor())) {
Expand Down Expand Up @@ -991,7 +993,7 @@ public static PythonFunctionType getFunctionType(PythonCompiledFunction pythonCo
}

private static void translatePythonBytecodeToMethod(MethodDescriptor method, String className, MethodVisitor methodVisitor,
PythonCompiledFunction pythonCompiledFunction, boolean isPythonLikeFunction, int pythonVersion, boolean isVirtual) {
PythonCompiledFunction pythonCompiledFunction, boolean isPythonLikeFunction, boolean isVirtual) {
// Apply Method Adapters, which reorder try blocks and check the bytecode to ensure it valid
methodVisitor = MethodVisitorAdapters.adapt(methodVisitor, method);

Expand Down Expand Up @@ -1057,6 +1059,7 @@ private static void translatePythonBytecodeToMethod(MethodDescriptor method, Str

FlowGraph flowGraph = FlowGraph.createFlowGraph(functionMetadata, initialStackMetadata, opcodeList);
triceo marked this conversation as resolved.
Show resolved Hide resolved
List<StackMetadata> stackMetadataForOpcodeIndex = flowGraph.getStackMetadataForOperations();

writeInstructionsForOpcodes(functionMetadata, stackMetadataForOpcodeIndex, opcodeList);

methodVisitor.visitLabel(end);
Expand Down Expand Up @@ -1140,6 +1143,19 @@ public static void writeInstructionsForOpcodes(FunctionMetadata functionMetadata
});
}

var requiredNullVariableSet = new TreeSet<Integer>();
triceo marked this conversation as resolved.
Show resolved Hide resolved
for (Opcode opcode : opcodeList) {
if (opcode instanceof LoadFastAndClearOpcode loadAndClearOpcode) {
requiredNullVariableSet.add(loadAndClearOpcode.getInstruction().arg());
}
}

for (var requiredNullVariable : requiredNullVariableSet) {
methodVisitor.visitInsn(Opcodes.ACONST_NULL);
methodVisitor.visitVarInsn(Opcodes.ASTORE,
stackMetadataForOpcodeIndex.get(0).localVariableHelper.getPythonLocalVariableSlot(requiredNullVariable));
}

for (int i = 0; i < opcodeList.size(); i++) {
StackMetadata stackMetadata = stackMetadataForOpcodeIndex.get(i);
PythonBytecodeInstruction instruction = pythonCompiledFunction.instructionList.get(i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ai.timefold.jpyinterpreter.opcodes.AbstractOpcode;
import ai.timefold.jpyinterpreter.opcodes.Opcode;
import ai.timefold.jpyinterpreter.opcodes.SelfOpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.controlflow.ReturnConstantValueOpcode;
import ai.timefold.jpyinterpreter.opcodes.controlflow.ReturnValueOpcode;
import ai.timefold.jpyinterpreter.opcodes.object.DeleteAttrOpcode;
import ai.timefold.jpyinterpreter.opcodes.object.LoadAttrOpcode;
Expand Down Expand Up @@ -1394,6 +1395,9 @@ public static PythonLikeType getPythonReturnTypeOfFunction(PythonCompiledFunctio
flowGraph.visitOperations(ReturnValueOpcode.class, (opcode, stackMetadata) -> {
possibleReturnTypeList.add(stackMetadata.getTOSType());
});
flowGraph.visitOperations(ReturnConstantValueOpcode.class, (opcode, stackMetadata) -> {
possibleReturnTypeList.add(opcode.getConstant(pythonCompiledFunction).$getGenericType());
});

return possibleReturnTypeList.stream()
.reduce(PythonLikeType::unifyWith)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
public final class PythonVersion implements Comparable<PythonVersion> {
private final int hexversion;

public static final PythonVersion PYTHON_3_9 = new PythonVersion(3, 9);
public static final PythonVersion PYTHON_3_10 = new PythonVersion(3, 10);
public static final PythonVersion PYTHON_3_11 = new PythonVersion(3, 11);
public static final PythonVersion PYTHON_3_12 = new PythonVersion(3, 12);

public static final PythonVersion MINIMUM_PYTHON_VERSION = PYTHON_3_9;
public static final PythonVersion MINIMUM_PYTHON_VERSION = PYTHON_3_10;

public PythonVersion(int hexversion) {
this.hexversion = hexversion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ public int getStackSize() {
return stackValueSources.size();
}

public List<PythonLikeType> getStackTypeList() {
return stackValueSources.stream().map(ValueSourceInfo::getValueType)
.collect(Collectors.toList());
}

/**
* Returns the list index for the given stack index (stack index is how many
* elements below TOS (i.e. 0 is TOS, 1 is TOS1)).
Expand Down Expand Up @@ -113,7 +108,8 @@ public PythonLikeType getTypeAtStackIndex(int index) {
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
// Unknown type
return BuiltinTypes.BASE_TYPE;
}

/**
Expand All @@ -126,20 +122,6 @@ public ValueSourceInfo getLocalVariableValueSource(int index) {
return localVariableValueSources.get(index);
}

/**
* Returns the type for the local variable in slot {@code index}
*
* @param index The slot
* @return The type for the local variable in the given slot
*/
public PythonLikeType getLocalVariableType(int index) {
ValueSourceInfo valueSourceInfo = localVariableValueSources.get(index);
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
}

/**
* Returns the value source for the cell variable in slot {@code index}
*
Expand All @@ -150,20 +132,6 @@ public ValueSourceInfo getCellVariableValueSource(int index) {
return cellVariableValueSources.get(index);
}

/**
* Returns the type for the cell variable in slot {@code index}
*
* @param index The slot
* @return The type for the cell variable in the given slot
*/
public PythonLikeType getCellVariableType(int index) {
ValueSourceInfo valueSourceInfo = cellVariableValueSources.get(index);
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
}

public PythonLikeType getTOSType() {
return getTypeAtStackIndex(0);
}
Expand Down Expand Up @@ -277,13 +245,6 @@ public StackMetadata pushTemps(PythonLikeType... types) {
return out;
}

public StackMetadata insertTemp(int tosIndex, PythonLikeType type) {
StackMetadata out = copy();
out.stackValueSources.add(stackValueSources.size() - tosIndex,
ValueSourceInfo.of(new OpcodeWithoutSource(), type));
return out;
}

/**
* Return a new StackMetadata with {@code types} as the stack;
* The original stack is cleared.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import ai.timefold.jpyinterpreter.types.PythonKnownFunctionType;
import ai.timefold.jpyinterpreter.types.PythonLikeFunction;
import ai.timefold.jpyinterpreter.types.PythonLikeType;
import ai.timefold.jpyinterpreter.types.PythonSlice;
import ai.timefold.jpyinterpreter.types.collections.PythonLikeList;
import ai.timefold.jpyinterpreter.types.errors.TypeError;

Expand Down Expand Up @@ -685,4 +686,52 @@ private static void binaryOpOverridingLeftIfSpecific(MethodVisitor methodVisitor
methodVisitor.visitLabel(ifDefined);
binaryOperator(methodVisitor, stackMetadata.localVariableHelper, operator);
}

public static void getSlice(FunctionMetadata functionMetadata, StackMetadata stackMetadata) {
// stack: ..., collection, start, end
var methodVisitor = functionMetadata.methodVisitor;
methodVisitor.visitTypeInsn(Opcodes.NEW, Type.getInternalName(PythonSlice.class));
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.POP);
methodVisitor.visitInsn(Opcodes.ACONST_NULL);
// stack: ..., collection, <uninit slice>, <uninit slice>, start, end, null
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(PythonSlice.class),
"<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PythonLikeObject.class),
Type.getType(PythonLikeObject.class),
Type.getType(PythonLikeObject.class)),
false);
// stack: ..., collection, slice
DunderOperatorImplementor.binaryOperator(methodVisitor, stackMetadata
.pop(3).pushTemps(stackMetadata.getTypeAtStackIndex(2), BuiltinTypes.SLICE_TYPE),
PythonBinaryOperator.GET_ITEM);
}

public static void storeSlice(FunctionMetadata functionMetadata, StackMetadata stackMetadata) {
// stack: ..., values, collection, start, end
var methodVisitor = functionMetadata.methodVisitor;
methodVisitor.visitTypeInsn(Opcodes.NEW, Type.getInternalName(PythonSlice.class));
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.POP);
methodVisitor.visitInsn(Opcodes.ACONST_NULL);
// stack: ..., values, collection, <uninit slice>, <uninit slice>, start, end, null
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(PythonSlice.class),
"<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PythonLikeObject.class),
Type.getType(PythonLikeObject.class),
Type.getType(PythonLikeObject.class)),
false);
// stack: ..., values, collection, slice
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.POP);
// stack: ..., slice, values, collection
methodVisitor.visitInsn(Opcodes.DUP_X2);
methodVisitor.visitInsn(Opcodes.POP);
// stack: ..., collection, slice, values
DunderOperatorImplementor.ternaryOperator(functionMetadata, stackMetadata
.pop(4).pushTemps(stackMetadata.getTypeAtStackIndex(2), BuiltinTypes.SLICE_TYPE,
stackMetadata.getTypeAtStackIndex(3)),
PythonTernaryOperator.SET_ITEM);
methodVisitor.visitInsn(Opcodes.POP);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import ai.timefold.jpyinterpreter.types.errors.PythonAssertionError;
import ai.timefold.jpyinterpreter.types.errors.PythonBaseException;
import ai.timefold.jpyinterpreter.types.errors.PythonTraceback;
import ai.timefold.jpyinterpreter.types.errors.StopIteration;
import ai.timefold.jpyinterpreter.types.numeric.PythonBoolean;
import ai.timefold.jpyinterpreter.types.numeric.PythonInteger;

Expand Down Expand Up @@ -447,4 +448,19 @@ public static void handleExceptionInWith(FunctionMetadata functionMetadata, Stac

localVariableHelper.freeLocal();
}

public static void getValueFromStopIterationOrReraise(FunctionMetadata functionMetadata, StackMetadata stackMetadata) {
var methodVisitor = functionMetadata.methodVisitor;
Label isStopIteration = new Label();
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, Type.getInternalName(StopIteration.class));
methodVisitor.visitJumpInsn(Opcodes.IFNE, isStopIteration);

ExceptionImplementor.reraise(methodVisitor);

methodVisitor.visitLabel(isStopIteration);
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(StopIteration.class));
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(StopIteration.class),
"getValue", Type.getMethodDescriptor(Type.getType(PythonLikeObject.class)), false);
}
}
Loading
Loading