Skip to content

Commit 0dc1273

Browse files
authored
Merge pull request #7 from Kreijstal/fix-wide-instruction-parser
Fix(parser): Correctly parse wide instructions
2 parents 502dd0c + 0358137 commit 0dc1273

5 files changed

Lines changed: 856 additions & 48 deletions

File tree

dissasembleClass.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,15 @@ function disassemble(ast, constantPool) {
303303
line += ` ${operands.index}`;
304304
} else if ("value" in operands) {
305305
line += ` ${operands.value}`;
306+
} else if ("byte" in operands) { // bipush immediate byte
307+
line += ` ${operands.byte}`;
308+
}
309+
310+
// For widened iinc (iinc_w) include constant
311+
if (opcodeName === 'iinc_w' && 'const' in operands) {
312+
line += ` ${operands.const}`;
313+
} else if (opcodeName === 'iinc' && 'const' in operands) {
314+
line += ` ${operands.index} ${operands.const}`; // existing narrow iinc already has index and const but index printed earlier
306315
}
307316

308317
output.push(line);
@@ -467,15 +476,36 @@ function parseClassFile(jsonObject, opcodeNames) {
467476
for (const inst of codeInfo.code.instructions) {
468477
const opcode = inst.instruction.opcode;
469478
const opcodeInfo = inst.instruction.info || {};
470-
const opcodeLength = opcodeInfo.length || 1;
471-
472-
const instruction = {
473-
pc,
474-
opcode,
475-
opcodeName: opcodeNames[opcode], // To be resolved later
476-
operands: opcodeInfo,
477-
comment: null
478-
};
479+
const opcodeLength = opcodeInfo.length || 1; // includes wide combined length from parser
480+
481+
// Handle wide specially: we want a synthetic widened opcode name like istore_w
482+
let instruction;
483+
if (opcode === 0xc4) { // wide
484+
const modified = opcodeInfo.modifiedOpcode;
485+
const widenedNameBase = opcodeNames[modified];
486+
// Map base mnemonic to widened form suffix, javap uses <mnemonic>_w (except iinc which becomes iinc_w)
487+
const widenedName = widenedNameBase + "_w";
488+
// Build operand structure
489+
const wideOperands = { index: opcodeInfo.index };
490+
if (modified === 0x84) { // iinc
491+
wideOperands.const = opcodeInfo.info.const; // 16-bit const already parsed
492+
}
493+
instruction = {
494+
pc,
495+
opcode: modified,
496+
opcodeName: widenedName,
497+
operands: wideOperands,
498+
comment: null
499+
};
500+
} else {
501+
instruction = {
502+
pc,
503+
opcode,
504+
opcodeName: opcodeNames[opcode], // To be resolved later
505+
operands: opcodeInfo,
506+
comment: null
507+
};
508+
}
479509

480510
// Resolve operands for specific opcodes
481511
if ("index" in opcodeInfo) {
@@ -533,8 +563,8 @@ function parseClassFile(jsonObject, opcodeNames) {
533563
}
534564
}
535565

536-
instructions.push(instruction);
537-
pc += opcodeLength; // Simplification; in reality, instruction lengths vary
566+
instructions.push(instruction);
567+
pc += opcodeLength; // length already accounts for wide expanded length
538568
}
539569

540570
methodInfo.code = {

generate_java.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const fs = require('fs');
2+
3+
// Create sources directory if it doesn't exist
4+
if (!fs.existsSync('sources')) {
5+
fs.mkdirSync('sources');
6+
}
7+
8+
let javaCode = `
9+
public class RealWide {
10+
public static void main(String[] args) {
11+
`;
12+
13+
for (let i = 0; i <= 256; i++) {
14+
javaCode += ` int var${i} = ${i};\n`;
15+
}
16+
17+
javaCode += ` System.out.println(var256);\n`;
18+
javaCode += ` }\n`;
19+
javaCode += `}\n`;
20+
21+
fs.writeFileSync('sources/RealWide.java', javaCode);

parsers.js

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ const AttributeInfo = Parser.start()
411411
});
412412

413413
const WideInstructionParser = Parser.start()
414-
.uint8("opcode", { assert: 0xc4 }) // wide opcode
415414
.uint8("modifiedOpcode")
416415
.uint16be("index")
417416
.choice("info", {
@@ -435,7 +434,9 @@ const WideInstructionParser = Parser.start()
435434
.buffer("length", {
436435
length: () => 0,
437436
formatter: function () {
438-
return 1 + 1 + 1 + 2 + (this.modifiedOpcode == 0x84 ? 2 : 0);
437+
// Total length INCLUDING the wide opcode itself:
438+
// 1 (wide) + 1 (modifiedOpcode) + 2 (index) + optional 2 (const for iinc)
439+
return 1 + 1 + 2 + (this.modifiedOpcode === 0x84 ? 2 : 0);
439440
},
440441
});
441442

@@ -1155,7 +1156,7 @@ const InstructionParser = Parser.start()
11551156
0xc3: Parser.start()
11561157
.namely("monitorexit")
11571158
.buffer("length", { length: () => 0, formatter: () => 1 }), // monitorexit
1158-
0xc4: "wide", // wide
1159+
0xc4: WideInstructionParser, // wide
11591160
0xc5: Parser.start()
11601161
.uint16be("index")
11611162
.uint8("dimensions")
@@ -1181,34 +1182,13 @@ const InstructionParser = Parser.start()
11811182
});
11821183

11831184
const BytecodeParser = Parser.start()
1184-
.useContextVars()
11851185
.array("instructions", {
1186-
type: Parser.start().choice("instruction", {
1187-
tag: function () {
1188-
switch (
1189-
this.opcode // Access the "opcode" property of the current item
1190-
) {
1191-
case 0xc4:
1192-
return 0; // WideInstructionParser
1193-
case 0xaa:
1194-
return 1; // TableswitchParser
1195-
case 0xab:
1196-
return 2; // LookupswitchParser
1197-
default:
1198-
return 3; // InstructionParser (default)
1199-
}
1200-
},
1201-
choices: [
1202-
// Use an array of choices (indexed numerically)
1203-
WideInstructionParser,
1204-
TableswitchParser,
1205-
LookupswitchParser,
1206-
InstructionParser,
1207-
],
1208-
}),
1209-
lengthInBytes: function (item) {
1210-
return this.$parent.code_length;
1211-
},
1186+
type: Parser.start().nest("instruction", {
1187+
type: InstructionParser
1188+
}),
1189+
lengthInBytes: function() {
1190+
return this.$parent.code_length;
1191+
}
12121192
});
12131193

12141194
CodeAttribute = CodeAttribute.useContextVars()
@@ -1301,7 +1281,7 @@ const ClassFile = Parser.start()
13011281
type: AttributeInfo,
13021282
length: "attributes_count",
13031283
});
1304-
module.exports= {
1284+
module.exports= {
13051285
CodeAttribute,
13061286
ConstantClassInfo,
13071287
ConstantFieldrefInfo,

0 commit comments

Comments
 (0)