3131import java .lang .constant .DynamicCallSiteDesc ;
3232import java .lang .constant .MethodTypeDesc ;
3333import java .util .ArrayList ;
34- import java .util .HashSet ;
3534import java .util .List ;
36- import java .util .Objects ;
3735import java .util .Optional ;
38- import java .util .Set ;
3936import java .util .function .Consumer ;
4037
4138import jdk .classfile .constantpool .ClassEntry ;
4946import jdk .classfile .constantpool .NameAndTypeEntry ;
5047import jdk .classfile .constantpool .Utf8Entry ;
5148import jdk .classfile .impl .AbstractInstruction ;
52- import jdk .classfile .impl .BlockCodeBuilder ;
49+ import jdk .classfile .impl .BlockCodeBuilderImpl ;
5350import jdk .classfile .impl .BytecodeHelpers ;
5451import jdk .classfile .impl .CatchBuilderImpl ;
5552import jdk .classfile .impl .ChainedCodeBuilder ;
9996 */
10097public sealed interface CodeBuilder
10198 extends ClassfileBuilder <CodeElement , CodeBuilder >
102- permits BlockCodeBuilder , ChainedCodeBuilder , TerminalCodeBuilder , NonterminalCodeBuilder {
99+ permits CodeBuilder . BlockCodeBuilder , ChainedCodeBuilder , TerminalCodeBuilder , NonterminalCodeBuilder {
103100
104101 /**
105102 * {@return the {@link CodeModel} representing the method body being transformed,
@@ -159,58 +156,140 @@ public sealed interface CodeBuilder
159156 int allocateLocal (TypeKind typeKind );
160157
161158 /**
162- * Add a lexical block to the method being built. Within this block, the
163- * {@link #startLabel()} and {@link #endLabel()} correspond to the start
164- * and end of the block.
159+ * A builder for blocks of code.
165160 */
166- default CodeBuilder block (Consumer <CodeBuilder > handler ) {
167- BlockCodeBuilder child = new BlockCodeBuilder (this );
161+ sealed interface BlockCodeBuilder extends CodeBuilder
162+ permits BlockCodeBuilderImpl {
163+ /**
164+ * {@return the label locating where control is passed back to the parent block.}
165+ * A branch to this label "break"'s out of the current block.
166+ * <p>
167+ * If an instruction occurring immediately after the built block's last instruction would
168+ * be reachable from that last instruction, then a {@linkplain #goto_ goto} instruction
169+ * targeting the "break" label is appended to the built block.
170+ */
171+ Label breakLabel ();
172+ }
173+
174+ /**
175+ * Add a lexical block to the method being built.
176+ * <p>
177+ * Within this block, the {@link #startLabel()} and {@link #endLabel()} correspond
178+ * to the start and end of the block, and the {@link BlockCodeBuilder#breakLabel()}
179+ * also corresponds to the end of the block.
180+ *
181+ * @param handler handler that receives a {@linkplain BlockCodeBuilder} to
182+ * generate the body of the lexical block.
183+ */
184+ default CodeBuilder block (Consumer <BlockCodeBuilder > handler ) {
185+ Label breakLabel = newLabel ();
186+ BlockCodeBuilderImpl child = new BlockCodeBuilderImpl (this , breakLabel );
168187 child .start ();
169188 handler .accept (child );
170189 child .end ();
190+ labelBinding (breakLabel );
171191 return this ;
172192 }
173193
174194 /**
175195 * Add an "if-then" block that is conditional on the boolean value
176196 * on top of the operand stack.
197+ * <p>
198+ * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds to the
199+ * end of that block.
177200 *
178- * @param thenHandler handler that receives a {@linkplain CodeBuilder } to
201+ * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder } to
179202 * generate the body of the {@code if}
180203 * @return this builder
181204 */
182- default CodeBuilder ifThen (Consumer <CodeBuilder > thenHandler ) {
183- BlockCodeBuilder thenBlock = new BlockCodeBuilder (this );
184- branchInstruction (Opcode .IFEQ , thenBlock .endLabel ());
205+ default CodeBuilder ifThen (Consumer <BlockCodeBuilder > thenHandler ) {
206+ return ifThen (Opcode .IFNE , thenHandler );
207+ }
208+
209+ /**
210+ * Add an "if-then" block that is conditional on the value(s) on top of the operand stack
211+ * in accordance with the given opcode.
212+ * <p>
213+ * The {@link BlockCodeBuilder#breakLabel()} for the "then" block corresponds to the
214+ * end of that block.
215+ *
216+ * @param opcode the operation code for a branch instructions that accepts one or two operands on the stack
217+ * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to
218+ * generate the body of the {@code if}
219+ * @return this builder
220+ * @throws java.lang.IllegalArgumentException if the operation code is not for a branch instruction that accepts
221+ * one or two operands
222+ */
223+ default CodeBuilder ifThen (Opcode opcode ,
224+ Consumer <BlockCodeBuilder > thenHandler ) {
225+ if (opcode .kind () != CodeElement .Kind .BRANCH || opcode .primaryTypeKind () == TypeKind .VoidType ) {
226+ throw new IllegalArgumentException ("Illegal branch opcode: " + opcode );
227+ }
228+
229+ Label breakLabel = newLabel ();
230+ BlockCodeBuilderImpl thenBlock = new BlockCodeBuilderImpl (this , breakLabel );
231+ branchInstruction (BytecodeHelpers .reverseBranchOpcode (opcode ), thenBlock .endLabel ());
185232 thenBlock .start ();
186233 thenHandler .accept (thenBlock );
187234 thenBlock .end ();
235+ labelBinding (breakLabel );
188236 return this ;
189237 }
190238
191239 /**
192240 * Add an "if-then-else" block that is conditional on the boolean value
193241 * on top of the operand stack.
242+ * <p>
243+ * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to the
244+ * end of the "else" block.
194245 *
195- * @param thenHandler handler that receives a {@linkplain CodeBuilder } to
246+ * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder } to
196247 * generate the body of the {@code if}
197- * @param elseHandler handler that receives a {@linkplain CodeBuilder } to
248+ * @param elseHandler handler that receives a {@linkplain BlockCodeBuilder } to
198249 * generate the body of the {@code else}
199250 * @return this builder
200251 */
201- default CodeBuilder ifThenElse (Consumer <CodeBuilder > thenHandler ,
202- Consumer <CodeBuilder > elseHandler ) {
203- BlockCodeBuilder thenBlock = new BlockCodeBuilder (this );
204- BlockCodeBuilder elseBlock = new BlockCodeBuilder (this );
205- branchInstruction (Opcode .IFEQ , elseBlock .startLabel ());
252+ default CodeBuilder ifThenElse (Consumer <BlockCodeBuilder > thenHandler ,
253+ Consumer <BlockCodeBuilder > elseHandler ) {
254+ return ifThenElse (Opcode .IFNE , thenHandler , elseHandler );
255+ }
256+
257+ /**
258+ * Add an "if-then-else" block that is conditional on the value(s) on top of the operand stack
259+ * in accordance with the given opcode.
260+ * <p>
261+ * The {@link BlockCodeBuilder#breakLabel()} for each block corresponds to the
262+ * end of the "else" block.
263+ *
264+ * @param opcode the operation code for a branch instructions that accepts one or two operands on the stack
265+ * @param thenHandler handler that receives a {@linkplain BlockCodeBuilder} to
266+ * generate the body of the {@code if}
267+ * @param elseHandler handler that receives a {@linkplain BlockCodeBuilder} to
268+ * generate the body of the {@code else}
269+ * @return this builder
270+ * @throws java.lang.IllegalArgumentException if the operation code is not for a branch instruction that accepts
271+ * one or two operands
272+ */
273+ default CodeBuilder ifThenElse (Opcode opcode ,
274+ Consumer <BlockCodeBuilder > thenHandler ,
275+ Consumer <BlockCodeBuilder > elseHandler ) {
276+ if (opcode .kind () != CodeElement .Kind .BRANCH || opcode .primaryTypeKind () == TypeKind .VoidType ) {
277+ throw new IllegalArgumentException ("Illegal branch opcode: " + opcode );
278+ }
279+
280+ Label breakLabel = newLabel ();
281+ BlockCodeBuilderImpl thenBlock = new BlockCodeBuilderImpl (this , breakLabel );
282+ BlockCodeBuilderImpl elseBlock = new BlockCodeBuilderImpl (this , breakLabel );
283+ branchInstruction (BytecodeHelpers .reverseBranchOpcode (opcode ), elseBlock .startLabel ());
206284 thenBlock .start ();
207285 thenHandler .accept (thenBlock );
208286 if (thenBlock .reachable ())
209- thenBlock .branchInstruction (Opcode .GOTO , elseBlock . endLabel ());
287+ thenBlock .branchInstruction (Opcode .GOTO , thenBlock . breakLabel ());
210288 thenBlock .end ();
211289 elseBlock .start ();
212290 elseHandler .accept (elseBlock );
213291 elseBlock .end ();
292+ labelBinding (breakLabel );
214293 return this ;
215294 }
216295
@@ -234,7 +313,7 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
234313 * @throws java.lang.IllegalArgumentException if an existing catch block catches an exception of the given type.
235314 * @see #catchingAll
236315 */
237- CatchBuilder catching (ClassDesc exceptionType , Consumer <CodeBuilder > catchHandler );
316+ CatchBuilder catching (ClassDesc exceptionType , Consumer <BlockCodeBuilder > catchHandler );
238317
239318 /**
240319 * Adds a "catch" block that catches all exceptions.
@@ -246,7 +325,7 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
246325 * @throws java.lang.IllegalArgumentException if an existing catch block catches all exceptions.
247326 * @see #catching
248327 */
249- void catchingAll (Consumer <CodeBuilder > catchAllHandler );
328+ void catchingAll (Consumer <BlockCodeBuilder > catchAllHandler );
250329 }
251330
252331 /**
@@ -260,12 +339,12 @@ sealed interface CatchBuilder permits CatchBuilderImpl {
260339 * @return this builder
261340 * @see CatchBuilder
262341 */
263- default CodeBuilder trying (Consumer <CodeBuilder > tryHandler ,
342+ default CodeBuilder trying (Consumer <BlockCodeBuilder > tryHandler ,
264343 Consumer <CatchBuilder > catchesHandler ) {
265344 Label tryCatchEnd = newLabel ();
266345
267346 // @@@ the tryHandler does not have access to tryCatchEnd
268- BlockCodeBuilder tryBlock = new BlockCodeBuilder (this );
347+ BlockCodeBuilderImpl tryBlock = new BlockCodeBuilderImpl (this , tryCatchEnd );
269348 tryBlock .start ();
270349 tryHandler .accept (tryBlock );
271350 tryBlock .end ();
0 commit comments