Skip to content

Commit 15342e0

Browse files
authored
Merge pull request #14 from Kreijstal/copilot/fix-93afb1a3-dbe0-4d74-a8f3-034e94207dc0
Fix interface static method parsing failure in jvm_parser
2 parents 8d02a2f + 9d26090 commit 15342e0

4 files changed

Lines changed: 101 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ newestast
1212
# Temporary test and demo files
1313
test_annotation_parsing.js
1414
demo_annotation_reflection.js
15+
.class

MinimalBug.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Save as `MinimalBug.java`
2+
public class MinimalBug {
3+
4+
interface MyInterface {
5+
static void doSomething() {
6+
// This is a static method in an interface (Java 8+)
7+
}
8+
}
9+
10+
public static void main(String[] args) {
11+
// This call to the static interface method creates a
12+
// MethodRef in the constant pool that the parser fails on.
13+
MyInterface.doSomething();
14+
}
15+
}

dissasembleClass.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ function disassemble(ast, constantPool) {
258258
opcodeName === "invokespecial" ||
259259
opcodeName === "invokestatic"
260260
) {
261-
const methodRef = getMethodRef(index);
261+
const methodRef = getMethodRef(index) || getInterfaceMethodRef(index);
262262
commentText = `Method ${methodRef.className}.${methodRef.name}:${methodRef.descriptor}`;
263263
} else if (opcodeName === "invokeinterface") {
264264
const methodRef = getInterfaceMethodRef(index);
@@ -538,7 +538,7 @@ function parseClassFile(jsonObject, opcodeNames) {
538538
const methodRef =
539539
opcode === 185
540540
? getInterfaceMethodRef(index)
541-
: getMethodRef(index);
541+
: getMethodRef(index) || getInterfaceMethodRef(index);
542542
instruction.comment = `Method ${methodRef.className}.${methodRef.name}:${methodRef.descriptor}`;
543543
} else if (
544544
opcode === 187 ||

test_interface_static_methods.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Test to verify that interface static method calls are parsed correctly.
5+
* This addresses the bug where jvm_parser fails with TypeError when parsing
6+
* class files containing calls to interface static methods.
7+
*/
8+
9+
const fs = require('fs');
10+
const { getAST, getDisassembled } = require('./lib');
11+
12+
function test_interface_static_methods() {
13+
console.log('Testing interface static method parsing...\n');
14+
15+
try {
16+
// Test with MinimalBug.class (contains interface static method call)
17+
console.log('✓ Testing MinimalBug.class (interface static method call)...');
18+
const minimalBugBytes = fs.readFileSync('MinimalBug.class');
19+
const minimalBugAST = getAST(minimalBugBytes);
20+
21+
if (!minimalBugAST.ast) {
22+
throw new Error('AST is missing');
23+
}
24+
25+
if (!minimalBugAST.ast.methods || minimalBugAST.ast.methods.length === 0) {
26+
throw new Error('Methods are missing from MinimalBug AST');
27+
}
28+
29+
console.log(` - Found ${minimalBugAST.ast.methods.length} method(s)`);
30+
31+
// Find the main method and check that it has bytecode
32+
const mainMethod = minimalBugAST.ast.methods.find(m => m.name === 'main');
33+
if (!mainMethod) {
34+
throw new Error('main method not found');
35+
}
36+
37+
console.log(' - Found main method');
38+
39+
// Check that main method has code with instructions
40+
if (!mainMethod.code || !mainMethod.code.instructions) {
41+
throw new Error('main method missing code or instructions');
42+
}
43+
44+
console.log(` - main method has ${mainMethod.code.instructions.length} instruction(s)`);
45+
46+
// Look for the invokestatic instruction that calls the interface method
47+
const invokeStaticInstr = mainMethod.code.instructions.find(instr =>
48+
instr.opcode === 184 && instr.comment && instr.comment.includes('doSomething')
49+
);
50+
51+
if (!invokeStaticInstr) {
52+
throw new Error('Expected invokestatic instruction calling doSomething not found');
53+
}
54+
55+
console.log(' - Found invokestatic instruction with interface method call');
56+
console.log(` - Instruction comment: ${invokeStaticInstr.comment}`);
57+
58+
// Test that we can also generate disassembled output
59+
console.log('✓ Testing disassembly of interface static method call...');
60+
const disassembled = getDisassembled(minimalBugBytes);
61+
62+
if (!disassembled.includes('doSomething')) {
63+
throw new Error('Disassembled output does not contain interface method name');
64+
}
65+
66+
console.log(' - Disassembly successful');
67+
68+
console.log('\n🎉 Interface static method parsing test passed!');
69+
return true;
70+
71+
} catch (error) {
72+
console.error('\n❌ Interface static method parsing test failed:', error.message);
73+
throw error;
74+
}
75+
}
76+
77+
// Run the test
78+
try {
79+
test_interface_static_methods();
80+
process.exit(0);
81+
} catch (error) {
82+
process.exit(1);
83+
}

0 commit comments

Comments
 (0)