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

feat: Add file and line numbers to exception traceback #28

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
31 changes: 13 additions & 18 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,23 @@ jobs:

steps:
# Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
- name: Checkout timefold-solver to access the scripts
- name: Checkout timefold-solver (PR) # Checkout the PR branch first, if it exists
id: checkout-solver
uses: actions/checkout@v4
continue-on-error: true
with:
path: './timefold-solver'
repository: 'TimefoldAI/timefold-solver'
- name: Find the proper timefold-solver repo and branch
env:
CHAIN_USER: ${{ github.event.pull_request.head.repo.owner.login }}
CHAIN_BRANCH: ${{ github.head_ref }}
CHAIN_REPO: "timefold-solver"
CHAIN_DEFAULT_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
shell: bash
run: |
./timefold-solver/.github/scripts/check_chain_repo.sh
rm -rf ./timefold-solver
- name: Checkout the proper timefold-solver branch
repository: ${{ github.actor }}/timefold-solver
ref: ${{ github.head_ref }}
path: ./timefold-solver
fetch-depth: 0 # Otherwise merge will fail on account of not having history.
- name: Checkout timefold-solver (main) # Checkout the main branch if the PR branch does not exist
if: steps.checkout-solver.outcome != 'success'
uses: actions/checkout@v4
with:
repository: ${{ env.TARGET_CHAIN_USER }}/${{ env.TARGET_CHAIN_REPO }}
ref: ${{ env.TARGET_CHAIN_BRANCH }}
path: './timefold-solver'
fetch-depth: 0 # Otherwise merge in the next step will fail on account of not having history.
repository: TimefoldAI/timefold-solver
ref: main
path: ./timefold-solver
fetch-depth: 0 # Otherwise merge will fail on account of not having history.
- name: Prevent stale fork of timefold-solver
env:
BLESSED_REPO: "timefold-solver"
Expand Down
31 changes: 13 additions & 18 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,23 @@ jobs:

steps:
# Need to check for stale repo, since Github is not aware of the build chain and therefore doesn't automate it.
- name: Checkout timefold-solver to access the scripts
- name: Checkout timefold-solver (PR) # Checkout the PR branch first, if it exists
id: checkout-solver
uses: actions/checkout@v4
continue-on-error: true
with:
path: './timefold-solver'
repository: 'TimefoldAI/timefold-solver'
- name: Find the proper timefold-solver repo and branch
env:
CHAIN_USER: ${{ github.event.pull_request.head.repo.owner.login }}
CHAIN_BRANCH: ${{ github.head_ref }}
CHAIN_REPO: "timefold-solver"
CHAIN_DEFAULT_BRANCH: ${{ endsWith(github.head_ref, '.x') && github.head_ref || 'main' }}
shell: bash
run: |
./timefold-solver/.github/scripts/check_chain_repo.sh
rm -rf ./timefold-solver
- name: Checkout the proper timefold-solver branch
repository: ${{ github.actor }}/timefold-solver
ref: ${{ github.head_ref }}
path: ./timefold-solver
fetch-depth: 0 # Otherwise merge will fail on account of not having history.
- name: Checkout timefold-solver (main) # Checkout the main branch if the PR branch does not exist
if: steps.checkout-solver.outcome != 'success'
uses: actions/checkout@v4
with:
repository: ${{ env.TARGET_CHAIN_USER }}/${{ env.TARGET_CHAIN_REPO }}
ref: ${{ env.TARGET_CHAIN_BRANCH }}
path: './timefold-solver'
fetch-depth: 0 # Otherwise merge in the next step will fail on account of not having history.
repository: TimefoldAI/timefold-solver
ref: main
path: ./timefold-solver
fetch-depth: 0 # Otherwise merge will fail on account of not having history.
- name: Prevent stale fork of timefold-solver
env:
BLESSED_REPO: "timefold-solver"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
final boolean isPythonLikeFunction =
methodDescriptor.getDeclaringClassInternalName().equals(Type.getInternalName(PythonLikeFunction.class));

classWriter.visitSource(pythonCompiledFunction.moduleFilePath, null);

createFields(classWriter);
createConstructor(classWriter, internalClassName);

Expand Down Expand Up @@ -289,6 +291,8 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
final boolean isPythonLikeFunction =
methodDescriptor.getDeclaringClassInternalName().equals(Type.getInternalName(PythonLikeFunction.class));

classWriter.visitSource(pythonCompiledFunction.moduleFilePath, null);

createFields(classWriter);
createConstructor(classWriter, internalClassName);

Expand All @@ -308,6 +312,7 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
null);

methodVisitor.visitCode();
visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 0; i < methodWithoutGenerics.getParameterCount(); i++) {
Type parameterType = Type.getType(methodWithoutGenerics.getParameterTypes()[i]);
Expand Down Expand Up @@ -349,6 +354,8 @@ public static <T> Class<T> translatePythonBytecodeToPythonWrapperClass(PythonCom
classWriter.visit(Opcodes.V11, Modifier.PUBLIC, internalClassName, null, Type.getInternalName(Object.class),
new String[] { Type.getInternalName(PythonLikeFunction.class) });

classWriter.visitSource(pythonCompiledFunction.moduleFilePath, null);

createFields(classWriter);
classWriter.visitField(Modifier.PUBLIC | Modifier.STATIC, PYTHON_WRAPPER_CODE_STATIC_FIELD_NAME,
Type.getDescriptor(OpaquePythonReference.class),
Expand All @@ -368,6 +375,7 @@ public static <T> Class<T> translatePythonBytecodeToPythonWrapperClass(PythonCom
null);

methodVisitor.visitCode();
visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, internalClassName, PYTHON_WRAPPER_FUNCTION_INSTANCE_FIELD_NAME,
Type.getDescriptor(PythonObjectWrapper.class));
Expand Down Expand Up @@ -417,6 +425,8 @@ public static <T> Class<T> forceTranslatePythonBytecodeToGeneratorClass(PythonCo
classWriter.visit(Opcodes.V11, Modifier.PUBLIC, internalClassName, null, Type.getInternalName(Object.class),
new String[] { methodDescriptor.getDeclaringClassInternalName() });

classWriter.visitSource(pythonCompiledFunction.moduleFilePath, null);

final boolean isPythonLikeFunction =
methodDescriptor.getDeclaringClassInternalName().equals(Type.getInternalName(PythonLikeFunction.class));

Expand Down Expand Up @@ -460,6 +470,7 @@ public static <T> Class<T> forceTranslatePythonBytecodeToGeneratorClass(PythonCo
null);

methodVisitor.visitCode();
visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 0; i < methodWithoutGenerics.getParameterCount(); i++) {
Type parameterType = Type.getType(methodWithoutGenerics.getParameterTypes()[i]);
Expand Down Expand Up @@ -494,6 +505,7 @@ private static void createConstructor(ClassWriter classWriter, String className)
null, null);
methodVisitor.visitCode();

visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Expand Down Expand Up @@ -574,6 +586,8 @@ private static void createConstructor(ClassWriter classWriter, String className)
Type.getType(PythonInterpreter.class)),
null, null);
methodVisitor.visitCode();

visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Expand Down Expand Up @@ -647,6 +661,7 @@ private static void createPythonWrapperConstructor(ClassWriter classWriter, Stri
null, null);
methodVisitor.visitCode();

visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Expand Down Expand Up @@ -759,6 +774,8 @@ private static void createPythonWrapperConstructor(ClassWriter classWriter, Stri
Type.getType(PythonInterpreter.class)),
null, null);
methodVisitor.visitCode();

visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Expand Down Expand Up @@ -1007,6 +1024,7 @@ private static void translatePythonBytecodeToMethod(MethodDescriptor method, Str
}
methodVisitor.visitCode();

visitGeneratedLineNumber(methodVisitor);
Label start = new Label();
Label end = new Label();

Expand Down Expand Up @@ -1172,6 +1190,12 @@ public static void writeInstructionsForOpcodes(FunctionMetadata functionMetadata
methodVisitor.visitLabel(label);
}

if (instruction.startsLine().isPresent()) {
Label label = new Label();
methodVisitor.visitLabel(label);
methodVisitor.visitLineNumber(instruction.startsLine().getAsInt(), label);
}

runAfterLabelAndBeforeArgumentors.accept(instruction);

bytecodeIndexToArgumentorsMap.getOrDefault(instruction.offset(), Collections.emptyList()).forEach(Runnable::run);
Expand Down Expand Up @@ -1361,4 +1385,10 @@ public static String getPythonBytecodeListing(PythonCompiledFunction pythonCompi
out.append("\nco_exceptiontable = ").append(pythonCompiledFunction.co_exceptiontable).append("\n");
return out.toString();
}

public static void visitGeneratedLineNumber(MethodVisitor methodVisitor) {
Label label = new Label();
methodVisitor.visitLabel(label);
methodVisitor.visitLineNumber(0, label);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public static PythonLikeType translatePythonClass(PythonCompiledClass pythonComp
classWriter.visit(Opcodes.V11, Modifier.PUBLIC, internalClassName, null,
superClassType.getJavaTypeInternalName(), interfaces);

classWriter.visitSource(pythonCompiledClass.moduleFilePath, null);

for (var annotation : pythonCompiledClass.annotations) {
annotation.addAnnotationTo(classWriter);
}
Expand Down Expand Up @@ -236,6 +238,7 @@ public static PythonLikeType translatePythonClass(PythonCompiledClass pythonComp
classWriter.visitMethod(Modifier.PUBLIC, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE),
null, null);
methodVisitor.visitCode();
PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(PythonInterpreter.class), "DEFAULT",
Type.getDescriptor(PythonInterpreter.class));
Expand All @@ -259,6 +262,7 @@ public static PythonLikeType translatePythonClass(PythonCompiledClass pythonComp
methodVisitor.visitParameter("subclassType", 0);

methodVisitor.visitCode();
PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
Expand Down Expand Up @@ -541,6 +545,8 @@ private static Class<?> createPythonWrapperMethod(String methodName, PythonCompi
classWriter.visit(Opcodes.V11, Modifier.PUBLIC, internalClassName, null,
Type.getInternalName(Object.class), new String[] { interfaceDeclaration.interfaceName });

classWriter.visitSource("<generated signature>", null);

classWriter.visitField(Modifier.PUBLIC | Modifier.FINAL, "$binaryType", Type.getDescriptor(PythonLikeType.class),
null, null);
classWriter.visitField(Modifier.STATIC | Modifier.PUBLIC, ARGUMENT_SPEC_INSTANCE_FIELD_NAME,
Expand All @@ -554,7 +560,7 @@ private static Class<?> createPythonWrapperMethod(String methodName, PythonCompi
null, null);

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE), false);
Expand All @@ -578,7 +584,7 @@ private static Class<?> createPythonWrapperMethod(String methodName, PythonCompi
}

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, internalClassName, "$binaryType",
Type.getDescriptor(PythonLikeType.class));
Expand Down Expand Up @@ -651,6 +657,8 @@ private static PythonLikeFunction createConstructor(String classInternalName,
Type.getInternalName(PythonLikeFunction.class)
});

classWriter.visitSource(initFunction != null ? initFunction.moduleFilePath : "<unknown>", null);

classWriter.visitField(Modifier.STATIC | Modifier.PUBLIC, ARGUMENT_SPEC_INSTANCE_FIELD_NAME,
Type.getDescriptor(ArgumentSpec.class),
null, null);
Expand All @@ -659,6 +667,7 @@ private static PythonLikeFunction createConstructor(String classInternalName,
classWriter.visitMethod(Modifier.PUBLIC, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE),
null, null);
methodVisitor.visitCode();
PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE), false);
Expand All @@ -674,13 +683,16 @@ private static PythonLikeFunction createConstructor(String classInternalName,
null, null);

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);
methodVisitor.visitTypeInsn(Opcodes.NEW, classInternalName);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, classInternalName, "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE), false);

if (initFunction != null) {
Label start = new Label();
methodVisitor.visitLabel(start);
methodVisitor.visitLineNumber(initFunction.getFirstLine(), start);
methodVisitor.visitInsn(Opcodes.DUP);

methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, constructorInternalClassName, ARGUMENT_SPEC_INSTANCE_FIELD_NAME,
Expand Down Expand Up @@ -844,6 +856,10 @@ private static void createClassMethod(PythonLikeType pythonLikeType, ClassWriter
addAnnotationsToMethod(function, methodVisitor);
methodVisitor.visitCode();

Label start = new Label();
methodVisitor.visitLabel(start);
methodVisitor.visitLineNumber(function.getFirstLine(), start);

methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, internalClassName, javaMethodName, interfaceDescriptor);

for (int i = 0; i < function.totalArgCount(); i++) {
Expand Down Expand Up @@ -880,6 +896,10 @@ private static void createInstanceOrStaticMethodBody(String internalClassName, S
addAnnotationsToMethod(function, methodVisitor);
methodVisitor.visitCode();

Label start = new Label();
methodVisitor.visitLabel(start);
methodVisitor.visitLineNumber(function.getFirstLine(), start);

methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, internalClassName, javaMethodName, interfaceDescriptor);
for (int i = 0; i < function.totalArgCount(); i++) {
methodVisitor.visitVarInsn(Opcodes.ALOAD, i);
Expand Down Expand Up @@ -929,6 +949,8 @@ public static void createGetAttribute(ClassWriter classWriter, String classInter

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
BytecodeSwitchImplementor.createStringSwitch(methodVisitor, instanceAttributes, 2, field -> {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
Expand Down Expand Up @@ -976,6 +998,8 @@ public static void createSetAttribute(ClassWriter classWriter, String classInter

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
BytecodeSwitchImplementor.createStringSwitch(methodVisitor, instanceAttributes, 3, field -> {
var type = fieldToType.get(field);
Expand Down Expand Up @@ -1023,6 +1047,8 @@ public static void createDeleteAttribute(ClassWriter classWriter, String classIn

methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
BytecodeSwitchImplementor.createStringSwitch(methodVisitor, instanceAttributes, 2, field -> {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
Expand Down Expand Up @@ -1063,6 +1089,8 @@ public static void createReadFromCPythonReference(ClassWriter classWriter, Strin
null);
methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName,
Expand Down Expand Up @@ -1157,6 +1185,8 @@ public static void createWriteToCPythonReference(ClassWriter classWriter, String
null);
methodVisitor.visitCode();

PythonBytecodeToJavaBytecodeTranslator.visitGeneratedLineNumber(methodVisitor);

methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitInsn(Opcodes.DUP);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
Expand Down Expand Up @@ -1305,6 +1335,8 @@ public static InterfaceDeclaration createInterfaceForFunctionSignature(FunctionS
classWriter.visit(Opcodes.V11, Modifier.PUBLIC | Modifier.INTERFACE | Modifier.ABSTRACT, internalClassName, null,
Type.getInternalName(Object.class), null);

classWriter.visitSource("<generated signature>", null);

Type returnType = Type.getType(functionSignature.returnType);
Type[] parameterTypes = new Type[functionSignature.parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ public class PythonCompiledClass {
*/
public String module;

/**
* The path to the file that defines the module.
*/
public String moduleFilePath;

/**
* The qualified name of the class. Does not include module.
*/
Expand Down
Loading