Skip to content

Commit e0e8494

Browse files
authored
Implement the wide opcode (#167)
The wide prefix (0xc4) was parsed as a zero-operand instruction, so its operands were misread as following opcodes and the interpreter hit a todo!(). Local-variable index opcodes (loads, stores, ret, iinc) now carry a u16 index; the narrow forms widen u8 to u16 and the wide prefix reads the widened index (plus a 16-bit constant for iinc). Methods with more than 256 locals or a wide iinc now run.
1 parent d98d6e0 commit e0e8494

5 files changed

Lines changed: 372 additions & 29 deletions

File tree

classfile/src/opcode.rs

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ pub enum Opcode {
1515
Aaload,
1616
Aastore,
1717
AconstNull,
18-
Aload(u8),
18+
Aload(u16),
1919
Anewarray(ConstantPoolReference),
2020
Areturn,
2121
Arraylength,
22-
Astore(u8),
22+
Astore(u16),
2323
Athrow,
2424
Baload,
2525
Bastore,
@@ -37,12 +37,12 @@ pub enum Opcode {
3737
Dcmpl,
3838
Dconst(u8),
3939
Ddiv,
40-
Dload(u8),
40+
Dload(u16),
4141
Dmul,
4242
Dneg,
4343
Drem,
4444
Dreturn,
45-
Dstore(u8),
45+
Dstore(u16),
4646
Dsub,
4747
Dup,
4848
DupX1,
@@ -60,12 +60,12 @@ pub enum Opcode {
6060
Fcmpl,
6161
Fconst(u8),
6262
Fdiv,
63-
Fload(u8),
63+
Fload(u16),
6464
Fmul,
6565
Fneg,
6666
Frem,
6767
Freturn,
68-
Fstore(u8),
68+
Fstore(u16),
6969
Fsub,
7070
Getfield(ConstantPoolReference),
7171
Getstatic(ConstantPoolReference),
@@ -99,8 +99,8 @@ pub enum Opcode {
9999
Ifle(i16),
100100
Ifnonnull(i16),
101101
Ifnull(i16),
102-
Iinc(u8, i8),
103-
Iload(u8),
102+
Iinc(u16, i16),
103+
Iload(u16),
104104
Imul,
105105
Ineg,
106106
Instanceof(ConstantPoolReference),
@@ -114,7 +114,7 @@ pub enum Opcode {
114114
Ireturn,
115115
Ishl,
116116
Ishr,
117-
Istore(u8),
117+
Istore(u16),
118118
Isub,
119119
Iushr,
120120
Ixor,
@@ -133,7 +133,7 @@ pub enum Opcode {
133133
LdcW(ConstantPoolReference),
134134
Ldc2W(ConstantPoolReference),
135135
Ldiv,
136-
Lload(u8),
136+
Lload(u16),
137137
Lmul,
138138
Lneg,
139139
Lookupswitch(i32, Vec<(i32, i32)>),
@@ -142,7 +142,7 @@ pub enum Opcode {
142142
Lreturn,
143143
Lshl,
144144
Lshr,
145-
Lstore(u8),
145+
Lstore(u16),
146146
Lsub,
147147
Lushr,
148148
Lxor,
@@ -156,14 +156,13 @@ pub enum Opcode {
156156
Pop2,
157157
Putfield(ConstantPoolReference),
158158
Putstatic(ConstantPoolReference),
159-
Ret(u8),
159+
Ret(u16),
160160
Return,
161161
Saload,
162162
Sastore,
163163
Sipush(i16),
164164
Swap,
165165
Tableswitch(i32, Vec<(i32, i32)>),
166-
Wide,
167166
}
168167

169168
impl Opcode {
@@ -176,7 +175,7 @@ impl Opcode {
176175
0x32 => success(Opcode::Aaload).parse(data),
177176
0x53 => success(Opcode::Aastore).parse(data),
178177
0x01 => success(Opcode::AconstNull).parse(data),
179-
0x19 => map(u8, Opcode::Aload).parse(data),
178+
0x19 => map(u8, |x| Opcode::Aload(x as u16)).parse(data),
180179
0x2a => success(Opcode::Aload(0)).parse(data),
181180
0x2b => success(Opcode::Aload(1)).parse(data),
182181
0x2c => success(Opcode::Aload(2)).parse(data),
@@ -187,7 +186,7 @@ impl Opcode {
187186
.parse(data),
188187
0xb0 => success(Opcode::Areturn).parse(data),
189188
0xbe => success(Opcode::Arraylength).parse(data),
190-
0x3a => map(u8, Opcode::Astore).parse(data),
189+
0x3a => map(u8, |x| Opcode::Astore(x as u16)).parse(data),
191190
0x4b => success(Opcode::Astore(0)).parse(data),
192191
0x4c => success(Opcode::Astore(1)).parse(data),
193192
0x4d => success(Opcode::Astore(2)).parse(data),
@@ -213,7 +212,7 @@ impl Opcode {
213212
0x0e => success(Opcode::Dconst(0)).parse(data),
214213
0x0f => success(Opcode::Dconst(1)).parse(data),
215214
0x6f => success(Opcode::Ddiv).parse(data),
216-
0x18 => map(u8, Opcode::Dload).parse(data),
215+
0x18 => map(u8, |x| Opcode::Dload(x as u16)).parse(data),
217216
0x26 => success(Opcode::Dload(0)).parse(data),
218217
0x27 => success(Opcode::Dload(1)).parse(data),
219218
0x28 => success(Opcode::Dload(2)).parse(data),
@@ -222,7 +221,7 @@ impl Opcode {
222221
0x77 => success(Opcode::Dneg).parse(data),
223222
0x73 => success(Opcode::Drem).parse(data),
224223
0xaf => success(Opcode::Dreturn).parse(data),
225-
0x39 => map(u8, Opcode::Dstore).parse(data),
224+
0x39 => map(u8, |x| Opcode::Dstore(x as u16)).parse(data),
226225
0x47 => success(Opcode::Dstore(0)).parse(data),
227226
0x48 => success(Opcode::Dstore(1)).parse(data),
228227
0x49 => success(Opcode::Dstore(2)).parse(data),
@@ -246,7 +245,7 @@ impl Opcode {
246245
0x0c => success(Opcode::Fconst(1)).parse(data),
247246
0x0d => success(Opcode::Fconst(2)).parse(data),
248247
0x6e => success(Opcode::Fdiv).parse(data),
249-
0x17 => map(u8, Opcode::Fload).parse(data),
248+
0x17 => map(u8, |x| Opcode::Fload(x as u16)).parse(data),
250249
0x22 => success(Opcode::Fload(0)).parse(data),
251250
0x23 => success(Opcode::Fload(1)).parse(data),
252251
0x24 => success(Opcode::Fload(2)).parse(data),
@@ -255,7 +254,7 @@ impl Opcode {
255254
0x76 => success(Opcode::Fneg).parse(data),
256255
0x72 => success(Opcode::Frem).parse(data),
257256
0xae => success(Opcode::Freturn).parse(data),
258-
0x38 => map(u8, Opcode::Fstore).parse(data),
257+
0x38 => map(u8, |x| Opcode::Fstore(x as u16)).parse(data),
259258
0x43 => success(Opcode::Fstore(0)).parse(data),
260259
0x44 => success(Opcode::Fstore(1)).parse(data),
261260
0x45 => success(Opcode::Fstore(2)).parse(data),
@@ -305,8 +304,8 @@ impl Opcode {
305304
0x9e => map(be_i16, Opcode::Ifle).parse(data),
306305
0xc7 => map(be_i16, Opcode::Ifnonnull).parse(data),
307306
0xc6 => map(be_i16, Opcode::Ifnull).parse(data),
308-
0x84 => map((u8, i8), |(index, constant)| Opcode::Iinc(index, constant)).parse(data),
309-
0x15 => map(u8, Opcode::Iload).parse(data),
307+
0x84 => map((u8, i8), |(index, constant)| Opcode::Iinc(index as u16, constant as i16)).parse(data),
308+
0x15 => map(u8, |x| Opcode::Iload(x as u16)).parse(data),
310309
0x1a => success(Opcode::Iload(0)).parse(data),
311310
0x1b => success(Opcode::Iload(1)).parse(data),
312311
0x1c => success(Opcode::Iload(2)).parse(data),
@@ -342,7 +341,7 @@ impl Opcode {
342341
0xac => success(Opcode::Ireturn).parse(data),
343342
0x78 => success(Opcode::Ishl).parse(data),
344343
0x7a => success(Opcode::Ishr).parse(data),
345-
0x36 => map(u8, Opcode::Istore).parse(data),
344+
0x36 => map(u8, |x| Opcode::Istore(x as u16)).parse(data),
346345
0x3b => success(Opcode::Istore(0)).parse(data),
347346
0x3c => success(Opcode::Istore(1)).parse(data),
348347
0x3d => success(Opcode::Istore(2)).parse(data),
@@ -369,7 +368,7 @@ impl Opcode {
369368
})
370369
.parse(data),
371370
0x6d => success(Opcode::Ldiv).parse(data),
372-
0x16 => map(u8, Opcode::Lload).parse(data),
371+
0x16 => map(u8, |x| Opcode::Lload(x as u16)).parse(data),
373372
0x1e => success(Opcode::Lload(0)).parse(data),
374373
0x1f => success(Opcode::Lload(1)).parse(data),
375374
0x20 => success(Opcode::Lload(2)).parse(data),
@@ -385,7 +384,7 @@ impl Opcode {
385384
0xad => success(Opcode::Lreturn).parse(data),
386385
0x79 => success(Opcode::Lshl).parse(data),
387386
0x7b => success(Opcode::Lshr).parse(data),
388-
0x37 => map(u8, Opcode::Lstore).parse(data),
387+
0x37 => map(u8, |x| Opcode::Lstore(x as u16)).parse(data),
389388
0x3f => success(Opcode::Lstore(0)).parse(data),
390389
0x40 => success(Opcode::Lstore(1)).parse(data),
391390
0x41 => success(Opcode::Lstore(2)).parse(data),
@@ -412,7 +411,7 @@ impl Opcode {
412411
Opcode::Putstatic(ConstantPoolReference::from_constant_pool(constant_pool, x as _))
413412
})
414413
.parse(data),
415-
0xa9 => map(u8, Opcode::Ret).parse(data),
414+
0xa9 => map(u8, |x| Opcode::Ret(x as u16)).parse(data),
416415
0xb1 => success(Opcode::Return).parse(data),
417416
0x35 => success(Opcode::Saload).parse(data),
418417
0x56 => success(Opcode::Sastore).parse(data),
@@ -427,10 +426,31 @@ impl Opcode {
427426
}
428427
})
429428
.parse(data),
430-
0xc4 => success(Opcode::Wide).parse(data),
429+
0xc4 => Self::parse_wide(data),
431430
_ => panic!("Unknown opcode: {:02x}", opcode),
432431
}
433432
}
433+
434+
// wide (0xc4): widens the local variable index of the following instruction to 16 bits
435+
fn parse_wide(data: &[u8]) -> IResult<&[u8], Self> {
436+
let (data, opcode) = u8(data)?;
437+
438+
match opcode {
439+
0x84 => map((be_u16, be_i16), |(index, constant)| Opcode::Iinc(index, constant)).parse(data),
440+
0x15 => map(be_u16, Opcode::Iload).parse(data),
441+
0x16 => map(be_u16, Opcode::Lload).parse(data),
442+
0x17 => map(be_u16, Opcode::Fload).parse(data),
443+
0x18 => map(be_u16, Opcode::Dload).parse(data),
444+
0x19 => map(be_u16, Opcode::Aload).parse(data),
445+
0x36 => map(be_u16, Opcode::Istore).parse(data),
446+
0x37 => map(be_u16, Opcode::Lstore).parse(data),
447+
0x38 => map(be_u16, Opcode::Fstore).parse(data),
448+
0x39 => map(be_u16, Opcode::Dstore).parse(data),
449+
0x3a => map(be_u16, Opcode::Astore).parse(data),
450+
0xa9 => map(be_u16, Opcode::Ret).parse(data),
451+
_ => panic!("Invalid wide opcode: {:02x}", opcode),
452+
}
453+
}
434454
}
435455

436456
#[cfg(test)]
@@ -487,4 +507,22 @@ mod test {
487507
assert!(remaining.is_empty());
488508
assert!(matches!(opcode, Opcode::Invokedynamic(_)));
489509
}
510+
511+
#[test]
512+
fn test_wide_iload_reads_u16_index() {
513+
// wide iload 0x0100, then nop — the trailing nop must not be misparsed
514+
let (remaining, opcode) = Opcode::parse(&[0xc4, 0x15, 0x01, 0x00, 0x00], 0, &constant_pool()).unwrap();
515+
516+
assert!(matches!(opcode, Opcode::Iload(0x0100)));
517+
assert_eq!(remaining, &[0x00]);
518+
}
519+
520+
#[test]
521+
fn test_wide_iinc_reads_u16_index_and_i16_const() {
522+
// wide iinc 0x012c, 1000
523+
let (remaining, opcode) = Opcode::parse(&[0xc4, 0x84, 0x01, 0x2c, 0x03, 0xe8], 0, &constant_pool()).unwrap();
524+
525+
assert!(matches!(opcode, Opcode::Iinc(0x012c, 1000)));
526+
assert!(remaining.is_empty());
527+
}
490528
}

jvm_rust/src/interpreter.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -933,9 +933,6 @@ impl Interpreter {
933933
stack_frame.operand_stack.push(value1);
934934
stack_frame.operand_stack.push(value2);
935935
}
936-
Opcode::Wide => {
937-
todo!()
938-
}
939936
}
940937

941938
Ok(ExecuteNext::Continue)

test_data/WideLocals.class

3.01 KB
Binary file not shown.

test_data/WideLocals.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2097

0 commit comments

Comments
 (0)