test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java
changeset 48826 c4d9d1b08e2e
equal deleted inserted replaced
48825:ef8a98bc71f8 48826:c4d9d1b08e2e
       
     1 /*
       
     2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package jdk.experimental.bytecode;
       
    25 
       
    26 import java.util.ArrayList;
       
    27 import java.util.HashMap;
       
    28 import java.util.Iterator;
       
    29 import java.util.LinkedHashMap;
       
    30 import java.util.LinkedList;
       
    31 import java.util.List;
       
    32 import java.util.Map;
       
    33 import java.util.TreeMap;
       
    34 import java.util.function.Consumer;
       
    35 
       
    36 public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> {
       
    37 
       
    38     JumpMode jumpMode = JumpMode.NARROW;
       
    39 
       
    40     Map<CharSequence, Integer> labels = new HashMap<>();
       
    41     List<PendingJump> pendingJumps = new LinkedList<>();
       
    42 
       
    43     class PendingJump {
       
    44         CharSequence label;
       
    45         int pc;
       
    46 
       
    47         PendingJump(CharSequence label, int pc) {
       
    48             this.label = label;
       
    49             this.pc = pc;
       
    50         }
       
    51 
       
    52         boolean resolve(CharSequence label, int offset) {
       
    53             if (this.label.equals(label)) {
       
    54                 //patch offset
       
    55                 code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc));
       
    56                 return true;
       
    57             } else {
       
    58                 return false;
       
    59             }
       
    60         }
       
    61     }
       
    62 
       
    63     public enum InvocationKind {
       
    64         INVOKESTATIC,
       
    65         INVOKEVIRTUAL,
       
    66         INVOKESPECIAL,
       
    67         INVOKEINTERFACE;
       
    68     }
       
    69 
       
    70     public enum FieldAccessKind {
       
    71         STATIC,
       
    72         INSTANCE;
       
    73     }
       
    74 
       
    75     public enum CondKind {
       
    76         EQ(0),
       
    77         NE(1),
       
    78         LT(2),
       
    79         GE(3),
       
    80         GT(4),
       
    81         LE(5);
       
    82 
       
    83         int offset;
       
    84 
       
    85         CondKind(int offset) {
       
    86             this.offset = offset;
       
    87         }
       
    88 
       
    89         public CondKind negate() {
       
    90             switch (this) {
       
    91                 case EQ:
       
    92                     return NE;
       
    93                 case NE:
       
    94                     return EQ;
       
    95                 case LT:
       
    96                     return GE;
       
    97                 case GE:
       
    98                     return LT;
       
    99                 case GT:
       
   100                     return LE;
       
   101                 case LE:
       
   102                     return GT;
       
   103                 default:
       
   104                     throw new IllegalStateException("Unknown cond");
       
   105             }
       
   106         }
       
   107     }
       
   108 
       
   109     static class WideJumpException extends RuntimeException {
       
   110         static final long serialVersionUID = 42L;
       
   111     }
       
   112 
       
   113     public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
       
   114         super(methodBuilder);
       
   115     }
       
   116 
       
   117     public C load(TypeTag type, int n) {
       
   118         if (type == TypeTag.Q) {
       
   119             return vload(n);
       
   120         } else {
       
   121             switch (n) {
       
   122                 case 0:
       
   123                     return emitOp(Opcode.ILOAD_0.at(type, 4));
       
   124                 case 1:
       
   125                     return emitOp(Opcode.ILOAD_1.at(type, 4));
       
   126                 case 2:
       
   127                     return emitOp(Opcode.ILOAD_2.at(type, 4));
       
   128                 case 3:
       
   129                     return emitOp(Opcode.ILOAD_3.at(type, 4));
       
   130                 default:
       
   131                     return emitWideIfNeeded(Opcode.ILOAD.at(type), n);
       
   132             }
       
   133         }
       
   134     }
       
   135 
       
   136     public C store(TypeTag type, int n) {
       
   137         if (type == TypeTag.Q) {
       
   138             return vstore(n);
       
   139         } else {
       
   140             switch (n) {
       
   141                 case 0:
       
   142                     return emitOp(Opcode.ISTORE_0.at(type, 4));
       
   143                 case 1:
       
   144                     return emitOp(Opcode.ISTORE_1.at(type, 4));
       
   145                 case 2:
       
   146                     return emitOp(Opcode.ISTORE_2.at(type, 4));
       
   147                 case 3:
       
   148                     return emitOp(Opcode.ISTORE_3.at(type, 4));
       
   149                 default:
       
   150                     return emitWideIfNeeded(Opcode.ISTORE.at(type), n);
       
   151             }
       
   152         }
       
   153     }
       
   154 
       
   155     public C arrayload(TypeTag type) {
       
   156         return emitOp(Opcode.IALOAD.at(type));
       
   157     }
       
   158 
       
   159     public C arraystore(TypeTag type, int n) {
       
   160         return emitOp(Opcode.IASTORE.at(type));
       
   161     }
       
   162 
       
   163     public C const_(int i) {
       
   164         switch (i) {
       
   165             case -1:
       
   166                 return iconst_m1();
       
   167             case 0:
       
   168                 return iconst_0();
       
   169             case 1:
       
   170                 return iconst_1();
       
   171             case 2:
       
   172                 return iconst_2();
       
   173             case 3:
       
   174                 return iconst_3();
       
   175             case 4:
       
   176                 return iconst_4();
       
   177             case 5:
       
   178                 return iconst_5();
       
   179             default:
       
   180                 if (i > 0 && i <= Byte.MAX_VALUE) {
       
   181                     return bipush(i);
       
   182                 } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
       
   183                     return sipush(i);
       
   184                 } else {
       
   185                     return ldc(i);
       
   186                 }
       
   187         }
       
   188     }
       
   189 
       
   190     public C const_(long l) {
       
   191         if (l == 0) {
       
   192             return lconst_0();
       
   193         } else if (l == 1) {
       
   194             return lconst_1();
       
   195         } else {
       
   196             return ldc(l);
       
   197         }
       
   198     }
       
   199 
       
   200     public C const_(float f) {
       
   201         if (f == 0) {
       
   202             return fconst_0();
       
   203         } else if (f == 1) {
       
   204             return fconst_1();
       
   205         } else if (f == 2) {
       
   206             return fconst_2();
       
   207         } else {
       
   208             return ldc(f);
       
   209         }
       
   210     }
       
   211 
       
   212     public C const_(double d) {
       
   213         if (d == 0) {
       
   214             return dconst_0();
       
   215         } else if (d == 1) {
       
   216             return dconst_1();
       
   217         } else {
       
   218             return ldc(d);
       
   219         }
       
   220     }
       
   221 
       
   222     public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
       
   223         switch (fak) {
       
   224             case INSTANCE:
       
   225                 return getfield(owner, name, type);
       
   226             case STATIC:
       
   227                 return getstatic(owner, name, type);
       
   228             default:
       
   229                 throw new IllegalStateException();
       
   230         }
       
   231     }
       
   232 
       
   233     public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
       
   234         switch (fak) {
       
   235             case INSTANCE:
       
   236                 return putfield(owner, name, type);
       
   237             case STATIC:
       
   238                 return putstatic(owner, name, type);
       
   239             default:
       
   240                 throw new IllegalStateException();
       
   241         }
       
   242     }
       
   243 
       
   244     public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) {
       
   245         switch (ik) {
       
   246             case INVOKESTATIC:
       
   247                 return invokestatic(owner, name, type, isInterface);
       
   248             case INVOKEVIRTUAL:
       
   249                 return invokevirtual(owner, name, type, isInterface);
       
   250             case INVOKESPECIAL:
       
   251                 return invokespecial(owner, name, type, isInterface);
       
   252             case INVOKEINTERFACE:
       
   253                 if (!isInterface) throw new AssertionError();
       
   254                 return invokeinterface(owner, name, type);
       
   255             default:
       
   256                 throw new IllegalStateException();
       
   257         }
       
   258     }
       
   259 
       
   260     public C add(TypeTag type) {
       
   261         return emitOp(Opcode.IADD.at(type));
       
   262     }
       
   263 
       
   264     public C sub(TypeTag type) {
       
   265         return emitOp(Opcode.ISUB.at(type));
       
   266     }
       
   267 
       
   268     public C mul(TypeTag type) {
       
   269         return emitOp(Opcode.IMUL.at(type));
       
   270     }
       
   271 
       
   272     public C div(TypeTag type) {
       
   273         return emitOp(Opcode.IDIV.at(type));
       
   274     }
       
   275 
       
   276     public C rem(TypeTag type) {
       
   277         return emitOp(Opcode.IREM.at(type));
       
   278     }
       
   279 
       
   280     public C neg(TypeTag type) {
       
   281         return emitOp(Opcode.INEG.at(type));
       
   282     }
       
   283 
       
   284     public C shl(TypeTag type) {
       
   285         return emitOp(Opcode.ISHL.at(type));
       
   286     }
       
   287 
       
   288     public C shr(TypeTag type) {
       
   289         return emitOp(Opcode.ISHR.at(type));
       
   290     }
       
   291 
       
   292     public C ushr(TypeTag type) {
       
   293         return emitOp(Opcode.ISHR.at(type));
       
   294     }
       
   295 
       
   296     public C and(TypeTag type) {
       
   297         return emitOp(Opcode.IAND.at(type));
       
   298     }
       
   299 
       
   300     public C or(TypeTag type) {
       
   301         return emitOp(Opcode.IOR.at(type));
       
   302     }
       
   303 
       
   304     public C xor(TypeTag type) {
       
   305         return emitOp(Opcode.IXOR.at(type));
       
   306     }
       
   307 
       
   308     public C return_(TypeTag type) {
       
   309         switch (type) {
       
   310             case V:
       
   311                 return return_();
       
   312             case Q:
       
   313                 return vreturn();
       
   314             default:
       
   315                 return emitOp(Opcode.IRETURN.at(type));
       
   316         }
       
   317     }
       
   318 
       
   319     @Override
       
   320     public LabelledTypedBuilder typed(TypeTag typeTag) {
       
   321         return super.typed(typeTag, _unused -> new LabelledTypedBuilder());
       
   322     }
       
   323 
       
   324     public class LabelledTypedBuilder extends TypedBuilder {
       
   325         public C if_acmpeq(CharSequence target) {
       
   326             return ifcmp(TypeTag.A, CondKind.EQ, target);
       
   327         }
       
   328 
       
   329         public C if_acmpne(CharSequence target) {
       
   330             return ifcmp(TypeTag.A, CondKind.NE, target);
       
   331         }
       
   332     }
       
   333 
       
   334     public C conv(TypeTag from, TypeTag to) {
       
   335         switch (from) {
       
   336             case B:
       
   337             case C:
       
   338             case S:
       
   339                 switch (to) {
       
   340                     case J:
       
   341                         return i2l();
       
   342                     case F:
       
   343                         return i2f();
       
   344                     case D:
       
   345                         return i2d();
       
   346                 }
       
   347                 break;
       
   348             case I:
       
   349                 switch (to) {
       
   350                     case J:
       
   351                         return i2l();
       
   352                     case F:
       
   353                         return i2f();
       
   354                     case D:
       
   355                         return i2d();
       
   356                     case B:
       
   357                         return i2b();
       
   358                     case C:
       
   359                         return i2c();
       
   360                     case S:
       
   361                         return i2s();
       
   362                 }
       
   363                 break;
       
   364             case J:
       
   365                 switch (to) {
       
   366                     case I:
       
   367                         return l2i();
       
   368                     case F:
       
   369                         return l2f();
       
   370                     case D:
       
   371                         return l2d();
       
   372                 }
       
   373                 break;
       
   374             case F:
       
   375                 switch (to) {
       
   376                     case I:
       
   377                         return f2i();
       
   378                     case J:
       
   379                         return f2l();
       
   380                     case D:
       
   381                         return f2d();
       
   382                 }
       
   383                 break;
       
   384             case D:
       
   385                 switch (to) {
       
   386                     case I:
       
   387                         return d2i();
       
   388                     case J:
       
   389                         return d2l();
       
   390                     case F:
       
   391                         return d2f();
       
   392                 }
       
   393                 break;
       
   394         }
       
   395         //no conversion is necessary - do nothing!
       
   396         return thisBuilder();
       
   397     }
       
   398 
       
   399     public C if_null(CharSequence label) {
       
   400         return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label);
       
   401     }
       
   402 
       
   403     public C if_nonnull(CharSequence label) {
       
   404         return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label);
       
   405     }
       
   406 
       
   407     public C ifcmp(TypeTag type, CondKind cond, CharSequence label) {
       
   408         switch (type) {
       
   409             case I:
       
   410                 return emitCondJump(Opcode.IF_ICMPEQ, cond, label);
       
   411             case A:
       
   412                 return emitCondJump(Opcode.IF_ACMPEQ, cond, label);
       
   413             case J:
       
   414                 return lcmp().emitCondJump(Opcode.IFEQ, cond, label);
       
   415             case D:
       
   416                 return dcmpg().emitCondJump(Opcode.IFEQ, cond, label);
       
   417             case F:
       
   418                 return fcmpg().emitCondJump(Opcode.IFEQ, cond, label);
       
   419             default:
       
   420                 throw new IllegalArgumentException("Bad cmp type");
       
   421         }
       
   422     }
       
   423 
       
   424     public C goto_(CharSequence label) {
       
   425         emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
       
   426         emitOffset(code, jumpMode, labelOffset(label));
       
   427         return thisBuilder();
       
   428     }
       
   429 
       
   430     protected int labelOffset(CharSequence label) {
       
   431         int pc = code.offset - 1;
       
   432         Integer labelPc = labels.get(label);
       
   433         if (labelPc == null) {
       
   434             addPendingJump(label, pc);
       
   435         }
       
   436         return labelPc == null ? 0 : (labelPc - pc);
       
   437     }
       
   438 
       
   439     public C label(CharSequence s) {
       
   440         int pc = code.offset;
       
   441         Object old = labels.put(s, pc);
       
   442         if (old != null) {
       
   443             throw new IllegalStateException("label already exists");
       
   444         }
       
   445         resolveJumps(s, pc);
       
   446         return thisBuilder();
       
   447     }
       
   448 
       
   449     //FIXME: address this jumpy mess - i.e. offset and state update work against each other!
       
   450     public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) {
       
   451         return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label);
       
   452     }
       
   453 
       
   454     public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) {
       
   455         if (jumpMode == JumpMode.NARROW) {
       
   456             emitOp(pos);
       
   457             emitOffset(code, jumpMode, labelOffset(label));
       
   458         } else {
       
   459             emitOp(neg);
       
   460             emitOffset(code, JumpMode.NARROW, 8);
       
   461             goto_w(labelOffset(label));
       
   462         }
       
   463         return thisBuilder();
       
   464     }
       
   465 
       
   466     void addPendingJump(CharSequence label, int pc) {
       
   467         pendingJumps.add(new PendingJump(label, pc));
       
   468     }
       
   469 
       
   470     void resolveJumps(CharSequence label, int pc) {
       
   471         Iterator<PendingJump> jumpsIt = pendingJumps.iterator();
       
   472         while (jumpsIt.hasNext()) {
       
   473             PendingJump jump = jumpsIt.next();
       
   474             if (jump.resolve(label, pc)) {
       
   475                 jumpsIt.remove();
       
   476             }
       
   477         }
       
   478     }
       
   479 
       
   480     @Override
       
   481     protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
       
   482         if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) {
       
   483             throw new WideJumpException();
       
   484         }
       
   485         super.emitOffset(buf, jumpMode, offset);
       
   486     }
       
   487 
       
   488     public C jsr(CharSequence label) {
       
   489         emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W);
       
   490         emitOffset(code, jumpMode, labelOffset(label));
       
   491         return thisBuilder();
       
   492     }
       
   493 
       
   494     @SuppressWarnings("unchecked")
       
   495     public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
       
   496         int start = code.offset;
       
   497         tryBlock.accept((C) this);
       
   498         int end = code.offset;
       
   499         CatchBuilder catchBuilder = makeCatchBuilder(start, end);
       
   500         catchBlocks.accept(catchBuilder);
       
   501         catchBuilder.build();
       
   502         return thisBuilder();
       
   503     }
       
   504 
       
   505     void clear() {
       
   506         code.offset = 0;
       
   507         catchers.offset = 0;
       
   508         ncatchers = 0;
       
   509         labels.clear();
       
   510         pendingJumps = null;
       
   511     }
       
   512 
       
   513     protected CatchBuilder makeCatchBuilder(int start, int end) {
       
   514         return new CatchBuilder(start, end);
       
   515     }
       
   516 
       
   517     public class CatchBuilder {
       
   518         int start, end;
       
   519 
       
   520         String endLabel = labelName();
       
   521 
       
   522         Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>();
       
   523         public Consumer<? super C> finalizer;
       
   524         List<Integer> pendingGaps = new ArrayList<>();
       
   525 
       
   526         public CatchBuilder(int start, int end) {
       
   527             this.start = start;
       
   528             this.end = end;
       
   529         }
       
   530 
       
   531         public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
       
   532             catchers.put(exc, catcher);
       
   533             return this;
       
   534         }
       
   535 
       
   536         public CatchBuilder withFinally(Consumer<? super C> finalizer) {
       
   537             this.finalizer = finalizer;
       
   538             return this;
       
   539         }
       
   540 
       
   541         @SuppressWarnings("unchecked")
       
   542         void build() {
       
   543             if (finalizer != null) {
       
   544                 finalizer.accept((C) MacroCodeBuilder.this);
       
   545             }
       
   546             goto_(endLabel);
       
   547             for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) {
       
   548                 emitCatch(catcher_entry.getKey(), catcher_entry.getValue());
       
   549             }
       
   550             if (finalizer != null) {
       
   551                 emitFinalizer();
       
   552             }
       
   553             resolveJumps(endLabel, code.offset);
       
   554         }
       
   555 
       
   556         @SuppressWarnings("unchecked")
       
   557         protected void emitCatch(S exc, Consumer<? super C> catcher) {
       
   558             int offset = code.offset;
       
   559             MacroCodeBuilder.this.withCatch(exc, start, end, offset);
       
   560             catcher.accept((C) MacroCodeBuilder.this);
       
   561             if (finalizer != null) {
       
   562                 int startFinalizer = code.offset;
       
   563                 finalizer.accept((C) MacroCodeBuilder.this);
       
   564                 pendingGaps.add(startFinalizer);
       
   565                 pendingGaps.add(code.offset);
       
   566             }
       
   567             goto_(endLabel);
       
   568         }
       
   569 
       
   570         @SuppressWarnings("unchecked")
       
   571         protected void emitFinalizer() {
       
   572             int offset = code.offset;
       
   573             pop();
       
   574             for (int i = 0; i < pendingGaps.size(); i += 2) {
       
   575                 MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset);
       
   576             }
       
   577             MacroCodeBuilder.this.withCatch(null, start, end, offset);
       
   578             finalizer.accept((C) MacroCodeBuilder.this);
       
   579         }
       
   580 
       
   581 //        @SuppressWarnings("unchecked")
       
   582 //        CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
       
   583 //            int offset = code.offset;
       
   584 //            MacroCodeBuilder.this.withCatch(exc, start, end, offset);
       
   585 //            catcher.accept((C)MacroCodeBuilder.this);
       
   586 //            return this;
       
   587 //        }
       
   588 //
       
   589 //        @SuppressWarnings("unchecked")
       
   590 //        CatchBuilder withFinally(Consumer<? super C> catcher) {
       
   591 //            int offset = code.offset;
       
   592 //            MacroCodeBuilder.this.withCatch(null, start, end, offset);
       
   593 //            catcher.accept((C)MacroCodeBuilder.this);
       
   594 //            return this;
       
   595 //        }
       
   596     }
       
   597 
       
   598     @SuppressWarnings("unchecked")
       
   599     public C switch_(Consumer<? super SwitchBuilder> consumer) {
       
   600         int start = code.offset;
       
   601         SwitchBuilder sb = makeSwitchBuilder();
       
   602         consumer.accept(sb);
       
   603         int nlabels = sb.cases.size();
       
   604         switch (sb.switchCode()) {
       
   605             case LOOKUPSWITCH: {
       
   606                 int[] lookupOffsets = new int[nlabels * 2];
       
   607                 int i = 0;
       
   608                 for (Integer v : sb.cases.keySet()) {
       
   609                     lookupOffsets[i] = v;
       
   610                     i += 2;
       
   611                 }
       
   612                 lookupswitch(0, lookupOffsets);
       
   613                 //backpatch lookup
       
   614                 int curr = code.offset - (8 * nlabels) - 8;
       
   615                 int defaultOffset = code.offset - start;
       
   616                 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
       
   617                 sb.defaultCase.accept((C) this);
       
   618                 curr += 12;
       
   619                 for (Consumer<? super C> case_ : sb.cases.values()) {
       
   620                     int offset = code.offset;
       
   621                     code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start));
       
   622                     case_.accept((C) this);
       
   623                     curr += 8;
       
   624                 }
       
   625                 break;
       
   626             }
       
   627             case TABLESWITCH: {
       
   628                 int[] tableOffsets = new int[sb.hi - sb.lo + 1];
       
   629                 tableswitch(sb.lo, sb.hi, 0, tableOffsets);
       
   630                 //backpatch table
       
   631                 int curr = code.offset - (4 * tableOffsets.length) - 12;
       
   632                 int defaultOffset = code.offset - start;
       
   633                 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
       
   634                 sb.defaultCase.accept((C) this);
       
   635                 curr += 12;
       
   636                 int lastCasePc = -1;
       
   637                 for (int i = sb.lo; i <= sb.hi; i++) {
       
   638                     Consumer<? super C> case_ = sb.cases.get(i);
       
   639                     if (case_ != null) {
       
   640                         lastCasePc = code.offset;
       
   641                         case_.accept((C) this);
       
   642                     }
       
   643                     int offset = lastCasePc - start;
       
   644                     code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset));
       
   645                     curr += 4;
       
   646                 }
       
   647             }
       
   648         }
       
   649         resolveJumps(sb.endLabel, code.offset);
       
   650         return thisBuilder();
       
   651     }
       
   652 
       
   653     private static int labelCount = 0;
       
   654 
       
   655     String labelName() {
       
   656         return "label" + labelCount++;
       
   657     }
       
   658 
       
   659     protected SwitchBuilder makeSwitchBuilder() {
       
   660         return new SwitchBuilder();
       
   661     }
       
   662 
       
   663     public class SwitchBuilder {
       
   664         Map<Integer, Consumer<? super C>> cases = new TreeMap<>();
       
   665         int lo = Integer.MAX_VALUE;
       
   666         int hi = Integer.MIN_VALUE;
       
   667         String endLabel = labelName();
       
   668 
       
   669         public Consumer<? super C> defaultCase;
       
   670 
       
   671         @SuppressWarnings("unchecked")
       
   672         public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
       
   673             if (value > hi) {
       
   674                 hi = value;
       
   675             }
       
   676             if (value < lo) {
       
   677                 lo = value;
       
   678             }
       
   679             if (!fallthrough) {
       
   680                 Consumer<? super C> prevCase = case_;
       
   681                 case_ = C -> {
       
   682                     prevCase.accept(C);
       
   683                     C.goto_(endLabel);
       
   684                 };
       
   685             }
       
   686             cases.put(value, case_);
       
   687             return this;
       
   688         }
       
   689 
       
   690         @SuppressWarnings("unchecked")
       
   691         public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
       
   692             if (this.defaultCase != null) {
       
   693                 throw new IllegalStateException("default already set");
       
   694             }
       
   695             this.defaultCase = defaultCase;
       
   696             return this;
       
   697         }
       
   698 
       
   699         Opcode switchCode() {
       
   700             int nlabels = cases.size();
       
   701             // Determine whether to issue a tableswitch or a lookupswitch
       
   702             // instruction.
       
   703             long table_space_cost = 4 + ((long) hi - lo + 1); // words
       
   704             long lookup_space_cost = 3 + 2 * (long) nlabels;
       
   705             return
       
   706                     nlabels > 0 &&
       
   707                             table_space_cost <= lookup_space_cost
       
   708                             ?
       
   709                             Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH;
       
   710         }
       
   711     }
       
   712 }