1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/// Constructs an `InsnList` using a declarative, Smali-like syntax.
///
/// This macro simplifies the creation of bytecode sequences by abstracting away
/// the verbose struct initialization.
///
/// # Syntax
/// Instructions are semicolon-separated. The general format is:
/// `[prefix] <OPCODE> [operands];`
///
/// ## Supported Instruction Types
///
/// * **Simple (No operands):** `OPCODE;`
/// * Example: `RETURN;`, `NOP;`
/// * **Int (Integer operand):** `int OPCODE <value>;`
/// * Example: `int BIPUSH 10;`
/// * **Var (Local variable index):** `var OPCODE <index>;`
/// * Example: `var ALOAD 0;`
/// * **Type (Type index/reference):** `type OPCODE <index>;`
/// * Example: `type NEW 5;`
/// * **Field (Field access):** `field OPCODE "owner", "name", "descriptor";`
/// * Example: `field GETSTATIC "java/lang/System", "out", "Ljava/io/PrintStream;";`
/// * **Method (Method invocation):** `method OPCODE "owner", "name", "descriptor";`
/// * Example: `method INVOKEVIRTUAL "java/io/PrintStream", "println", "(Ljava/lang/String;)V";`
/// * **Ldc (String constant):** `ldc "string_value";`
/// * Example: `ldc "Hello World";`
/// * **Jump (Offset):** `jump OPCODE <offset>;`
/// * Example: `jump GOTO 10;`
/// * **Iinc (Variable increment):** `iinc OPCODE <var_index>, <increment>;`
/// * Example: `iinc IINC 1, 5;`
///
/// ## Example
///
/// ```rust
///
/// use rust_asm::{opcodes, insn_list, insn::{Insn, LdcValue, MemberRef}};
/// // execute the macro
/// let list = insn_list! {
/// [NOP]
/// [int BIPUSH 42]
/// [var ALOAD 1]
/// [type NEW 5]
/// [field GETSTATIC "java/lang/System", "out", "Ljava/io/PrintStream;"]
/// [method INVOKEVIRTUAL "java/io/PrintStream", "println", "(Ljava/lang/String;)V"]
/// [ldc "Hello Macro"]
/// [jump GOTO -10]
/// [iinc IINC 2, 1]
/// [RETURN]
/// };
///
/// let insns = list.into_insns();
///
/// // Basic assertion on length
/// assert_eq!(insns.len(), 10, "Expected 10 instructions in the list");
///
/// // Detailed assertions per instruction
/// // 1. NOP
/// if let Insn::Simple(node) = &insns[0] {
/// assert_eq!(node.opcode, opcodes::NOP);
/// } else {
/// panic!("Expected Simple Insn at index 0");
/// }
///
/// // 2. BIPUSH 42
/// if let Insn::Int(node) = &insns[1] {
/// assert_eq!(node.insn.opcode, opcodes::BIPUSH);
/// assert_eq!(node.operand, 42);
/// } else {
/// panic!("Expected Int Insn at index 1");
/// }
///
/// // 3. ALOAD 1
/// if let Insn::Var(node) = &insns[2] {
/// assert_eq!(node.insn.opcode, opcodes::ALOAD);
/// assert_eq!(node.var_index, 1);
/// } else {
/// panic!("Expected Var Insn at index 2");
/// }
///
/// // 4. NEW 5
/// if let Insn::Type(node) = &insns[3] {
/// assert_eq!(node.insn.opcode, opcodes::NEW);
/// assert_eq!(node.type_index, 5);
/// } else {
/// panic!("Expected Type Insn at index 3");
/// }
///
/// // 5. GETSTATIC (Field)
/// if let Insn::Field(node) = &insns[4] {
/// assert_eq!(node.insn.opcode, opcodes::GETSTATIC);
/// if let MemberRef::Symbolic {
/// owner,
/// name,
/// descriptor,
/// } = &node.field_ref
/// {
/// assert_eq!(owner, "java/lang/System");
/// assert_eq!(name, "out");
/// assert_eq!(descriptor, "Ljava/io/PrintStream;");
/// } else {
/// panic!("Expected Symbolic reference for Field");
/// }
/// } else {
/// panic!("Expected Field Insn at index 4");
/// }
///
/// // 7. LDC "Hello Macro"
/// if let Insn::Ldc(node) = &insns[6] {
/// assert_eq!(node.insn.opcode, opcodes::LDC);
/// if let LdcValue::String(val) = &node.value {
/// assert_eq!(val, "Hello Macro");
/// } else {
/// panic!("Expected String value for Ldc");
/// }
/// } else {
/// panic!("Expected Ldc Insn at index 6");
/// }
///
/// // 8. GOTO -10
/// if let Insn::Jump(node) = &insns[7] {
/// assert_eq!(node.insn.opcode, opcodes::GOTO);
/// assert_eq!(node.offset, -10);
/// } else {
/// panic!("Expected Jump Insn at index 7");
/// }
///
/// // 9. IINC 2, 1
/// if let Insn::Iinc(node) = &insns[8] {
/// assert_eq!(node.insn.opcode, opcodes::IINC);
/// assert_eq!(node.var_index, 2);
/// assert_eq!(node.increment, 1);
/// } else {
/// panic!("Expected Iinc Insn at index 8");
/// }
/// ```
};
// Simple (e.g., [NOP], [RETURN])
=> ;
// Int (e.g., [int BIPUSH 10])
=> ;
// Var (e.g., [var ALOAD 0])
=> ;
// Type (e.g., [type NEW 5])
=> ;
// Field (e.g., [field GETSTATIC "owner", "name", "desc"])
=> ;
// Method (e.g., [method INVOKEVIRTUAL "owner", "name", "desc"])
=> ;
// Ldc (e.g., [ldc "hello"])
=> ;
// Ldc Index (e.g., [ldc_idx LDC 5])
=> ;
// Jump (e.g., [jump GOTO 10])
=> ;
// Iinc (e.g., [iinc IINC 1, 5])
=> ;
}