1 /*
   2  * Copyright (c) 2016, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.experimental.bytecode;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodType;
  30 import java.util.ArrayList;
  31 import java.util.HashMap;
  32 import java.util.Iterator;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Map.Entry;
  36 import java.util.Vector;
  37 import java.util.function.Consumer;
  38 import java.util.function.Supplier;
  39 import java.util.function.ToIntFunction;
  40 
  41 public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> {
  42 
  43     State lastStackMapState;
  44     int lastStackMapPc = -1;
  45     Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>();
  46     protected State state;
  47     int depth = 0;
  48     int currLocalOffset = 0;
  49 
  50     class StatefulPendingJump extends PendingJump {
  51 
  52         State state;
  53 
  54         StatefulPendingJump(CharSequence label, int pc, State state) {
  55             super(label, pc);
  56             this.state = state;
  57         }
  58 
  59         @Override
  60         boolean resolve(CharSequence label, int pc) {
  61             boolean b = super.resolve(label, pc);
  62             if (b) {
  63                 TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
  64             }
  65             return b;
  66         }
  67     }
  68 
  69     class LocalVarInfo {
  70         CharSequence name;
  71         int offset;
  72         int depth;
  73         TypeTag type;
  74 
  75         LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) {
  76             this.name = name;
  77             this.offset = offset;
  78             this.depth = depth;
  79             this.type = type;
  80         }
  81     }
  82 
  83     public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
  84         super(methodBuilder);
  85         T t = methodBuilder.desc;
  86         state = new State();
  87         if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) {
  88             T clazz = typeHelper.type(methodBuilder.thisClass);
  89             state.load(clazz, currLocalOffset++); //TODO: uninit??
  90         }
  91         Iterator<T> paramsIt = typeHelper.parameterTypes(t);
  92         while (paramsIt.hasNext()) {
  93             T p = paramsIt.next();
  94             state.load(p, currLocalOffset);
  95             currLocalOffset += typeHelper.tag(p).width;
  96         }
  97         lastStackMapState = state.dup();
  98         stacksize = state.stack.size();
  99         localsize = state.locals.size();
 100     }
 101 
 102     @Override
 103     protected C emitOp(Opcode opcode, Object optPoolValue) {
 104         updateState(opcode, optPoolValue);
 105         return super.emitOp(opcode, optPoolValue);
 106     }
 107 
 108     @Override
 109     protected SwitchBuilder makeSwitchBuilder() {
 110         return new TypedSwitchBuilder();
 111     }
 112 
 113     class TypedSwitchBuilder extends SwitchBuilder {
 114 
 115         @Override
 116         public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
 117             super.withCase(value, c -> {
 118                 withLocalScope(() -> {
 119                     State prevState = state;
 120                     state = prevState.dup();
 121                     emitStackMap(c.offset());
 122                     case_.accept(c);
 123                     state = prevState;
 124                 });
 125             }, fallthrough);
 126             return this;
 127         }
 128 
 129         @Override
 130         public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
 131             super.withDefault(c -> {
 132                 withLocalScope(() -> {
 133                     State prevState = state;
 134                     state = prevState.dup();
 135                     emitStackMap(c.offset());
 136                     defaultCase.accept(c);
 137                     state = prevState;
 138                 });
 139             });
 140             return this;
 141         }
 142     }
 143 
 144     @Override
 145     public StatefulTypedBuilder typed(TypeTag tag) {
 146         return super.typed(tag, StatefulTypedBuilder::new);
 147     }
 148 
 149     public class StatefulTypedBuilder extends LabelledTypedBuilder {
 150 
 151         TypeTag tag;
 152 
 153         StatefulTypedBuilder(TypeTag tag) {
 154             this.tag = tag;
 155         }
 156 
 157         @Override
 158         public C astore_0() {
 159             return storeAndUpdate(super::astore_0);
 160         }
 161 
 162         @Override
 163         public C astore_1() {
 164             return storeAndUpdate(super::astore_1);
 165         }
 166 
 167         @Override
 168         public C astore_2() {
 169             return storeAndUpdate(super::astore_2);
 170         }
 171 
 172         @Override
 173         public C astore_3() {
 174             return storeAndUpdate(super::astore_3);
 175         }
 176 
 177         @Override
 178         public C astore(int n) {
 179             return storeAndUpdate(() -> super.astore(n));
 180         }
 181 
 182         @Override
 183         public C aastore() {
 184             return storeAndUpdate(super::aastore);
 185         }
 186 
 187         @Override
 188         public C areturn() {
 189             state.pop(tag);
 190             state.push(typeHelper.nullType());
 191             return super.areturn();
 192         }
 193 
 194         @Override
 195         public C anewarray(S s) {
 196             super.anewarray(s);
 197             state.pop();
 198             state.push(typeHelper.arrayOf(typeHelper.type(s)));
 199             return thisBuilder();
 200         }
 201 
 202         @Override
 203         public C anewvaluearray(S s) {
 204             super.anewvaluearray(s);
 205             state.pop();
 206             state.push(typeHelper.arrayOf(typeHelper.valueType(s)));
 207             return thisBuilder();
 208         }
 209 
 210         @Override
 211         public C aconst_null() {
 212             super.aconst_null();
 213             state.pop();
 214             state.push(tag);
 215             return thisBuilder();
 216         }
 217 
 218         public C if_acmpeq(CharSequence label) {
 219             return jumpAndUpdate(() -> super.if_acmpeq(label));
 220         }
 221 
 222         public C if_acmpne(CharSequence label) {
 223             return jumpAndUpdate(() -> super.if_acmpne(label));
 224         }
 225 
 226         private C storeAndUpdate(Supplier<C> op) {
 227             state.pop(tag);
 228             state.push(typeHelper.nullType());
 229             return op.get();
 230         }
 231 
 232         private C jumpAndUpdate(Supplier<C> op) {
 233             state.pop(tag);
 234             state.pop(tag);
 235             state.push(typeHelper.nullType());
 236             state.push(typeHelper.nullType());
 237             return op.get();
 238         }
 239     }
 240 
 241     public class State {
 242         public final ArrayList<T> stack;
 243         public final Vector<T> locals;
 244         boolean alive;
 245 
 246         State(ArrayList<T> stack, Vector<T> locals) {
 247             this.stack = stack;
 248             this.locals = locals;
 249             this.alive = true;
 250         }
 251 
 252         State() {
 253             this(new ArrayList<>(), new Vector<>());
 254         }
 255 
 256         void push(TypeTag tag) {
 257             switch (tag) {
 258                 case A:
 259                 case V:
 260                     throw new IllegalStateException("Bad type tag");
 261                 default:
 262                     push(typeHelper.fromTag(tag));
 263             }
 264         }
 265 
 266         void push(T t) {
 267             stack.add(t);
 268             if (width(t) == 2) {
 269                 stack.add(null);
 270             }
 271             if (stack.size() > stacksize) {
 272                 stacksize = stack.size();
 273             }
 274         }
 275 
 276         T peek() {
 277             return stack.get(stack.size() - 1);
 278         }
 279 
 280         T tosType() {
 281             T tos = peek();
 282             if (tos == null) {
 283                 //double slot
 284                 tos = stack.get(stack.size() - 2);
 285             }
 286             return tos;
 287         }
 288 
 289         T popInternal() {
 290             return stack.remove(stack.size() - 1);
 291         }
 292 
 293         @SuppressWarnings("unchecked")
 294         T pop() {
 295             if (stack.size() == 0 || peek() == null) throw new IllegalStateException();
 296             return popInternal();
 297         }
 298 
 299         T pop2() {
 300             T o = stack.get(stack.size() - 2);
 301             TypeTag t = typeHelper.tag(o);
 302             if (t.width != 2) throw new IllegalStateException();
 303             popInternal();
 304             popInternal();
 305             return o;
 306         }
 307 
 308         T pop(TypeTag t) {
 309             return (t.width() == 2) ?
 310                 pop2() : pop();
 311         }
 312 
 313         void load(TypeTag tag, int index) {
 314             if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag");
 315             load(typeHelper.fromTag(tag), index);
 316         }
 317 
 318         void load(T t, int index) {
 319             ensureDefined(index);
 320             locals.set(index, t);
 321             if (width(t) == 2) {
 322                 locals.add(null);
 323             }
 324             if (locals.size() > localsize) {
 325                 localsize = locals.size();
 326             }
 327         }
 328 
 329         void ensureDefined(int index) {
 330             if (index >= locals.size()) {
 331                 locals.setSize(index + 1);
 332             }
 333         }
 334 
 335         State dup() {
 336             State newState = new State(new ArrayList<>(stack), new Vector<>(locals));
 337             return newState;
 338         }
 339 
 340         State merge(State that) {
 341             if (!alive) { return that; }
 342             if (that.stack.size() != stack.size()) {
 343                 throw new IllegalStateException("Bad stack size at merge point");
 344             }
 345             for (int i = 0; i < stack.size(); i++) {
 346                 T t1 = stack.get(i);
 347                 T t2 = that.stack.get(i);
 348                 stack.set(i, merge(t1, t2, "Bad stack type at merge point"));
 349             }
 350             int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size();
 351             for (int i = 0; i < nlocals; i++) {
 352                 T t1 = locals.get(i);
 353                 T t2 = that.locals.get(i);
 354                 locals.set(i, merge(t1, t2, "Bad local type at merge point"));
 355             }
 356             if (locals.size() > nlocals) {
 357                 for (int i = nlocals; i < locals.size(); i++) {
 358                     locals.remove(i);
 359                 }
 360             }
 361             return this;
 362         }
 363 
 364         T merge(T t1, T t2, String msg) {
 365             if (t1 == null && t2 == null) {
 366                 return t1;
 367             }
 368             T res;
 369             TypeTag tag1 = typeHelper.tag(t1);
 370             TypeTag tag2 = typeHelper.tag(t2);
 371             if (tag1 != TypeTag.A && tag2 != TypeTag.A) {
 372                 res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2));
 373             } else if (t1 == typeHelper.nullType()) {
 374                 res = t2;
 375             } else if (t2 == typeHelper.nullType()) {
 376                 res = t1;
 377             } else {
 378                 res = typeHelper.commonSupertype(t1, t2);
 379             }
 380             if (res == null) {
 381                 throw new IllegalStateException(msg);
 382             }
 383             return res;
 384         }
 385 
 386         @Override
 387         public String toString() {
 388             return String.format("[locals = %s, stack = %s]", locals, stack);
 389         }
 390     }
 391 
 392     int width(T o) {
 393         return o == typeHelper.nullType() ?
 394                 TypeTag.A.width() :
 395                 typeHelper.tag(o).width;
 396     }
 397 
 398     @SuppressWarnings("unchecked")
 399     public void updateState(Opcode op, Object optValue) {
 400         switch (op) {
 401             case AALOAD:
 402                 state.pop();
 403                 state.push(typeHelper.elemtype(state.pop()));
 404                 break;
 405             case GOTO_:
 406                 state.alive = false;
 407                 break;
 408             case NOP:
 409             case IINC:
 410             case INEG:
 411             case LNEG:
 412             case FNEG:
 413             case DNEG:
 414                 break;
 415             case ACONST_NULL:
 416                 state.push(typeHelper.nullType());
 417                 break;
 418             case ICONST_M1:
 419             case ICONST_0:
 420             case ICONST_1:
 421             case ICONST_2:
 422             case ICONST_3:
 423             case ICONST_4:
 424             case ICONST_5:
 425                 state.push(TypeTag.I);
 426                 break;
 427             case LCONST_0:
 428             case LCONST_1:
 429                 state.push(TypeTag.J);
 430                 break;
 431             case FCONST_0:
 432             case FCONST_1:
 433             case FCONST_2:
 434                 state.push(TypeTag.F);
 435                 break;
 436             case DCONST_0:
 437             case DCONST_1:
 438                 state.push(TypeTag.D);
 439                 break;
 440             case ILOAD_0:
 441             case FLOAD_0:
 442             case ALOAD_0:
 443             case LLOAD_0:
 444             case DLOAD_0:
 445                 state.push(state.locals.get(0));
 446                 break;
 447             case ILOAD_1:
 448             case FLOAD_1:
 449             case ALOAD_1:
 450             case LLOAD_1:
 451             case DLOAD_1:
 452                 state.push(state.locals.get(1));
 453                 break;
 454             case ILOAD_2:
 455             case FLOAD_2:
 456             case ALOAD_2:
 457             case LLOAD_2:
 458             case DLOAD_2:
 459                 state.push(state.locals.get(2));
 460                 break;
 461             case ILOAD_3:
 462             case FLOAD_3:
 463             case ALOAD_3:
 464             case LLOAD_3:
 465             case DLOAD_3:
 466                 state.push(state.locals.get(3));
 467                 break;
 468             case ILOAD:
 469             case FLOAD:
 470             case ALOAD:
 471             case LLOAD:
 472             case DLOAD:
 473                 state.push(state.locals.get((Integer) optValue));
 474                 break;
 475             case IALOAD:
 476             case BALOAD:
 477             case CALOAD:
 478             case SALOAD:
 479                 state.pop();
 480                 state.pop();
 481                 state.push(TypeTag.I);
 482                 break;
 483             case LALOAD:
 484                 state.pop();
 485                 state.pop();
 486                 state.push(TypeTag.J);
 487                 break;
 488             case FALOAD:
 489                 state.pop();
 490                 state.pop();
 491                 state.push(TypeTag.F);
 492                 break;
 493             case DALOAD:
 494                 state.pop();
 495                 state.pop();
 496                 state.push(TypeTag.D);
 497                 break;
 498             case ISTORE_0:
 499             case FSTORE_0:
 500             case ASTORE_0:
 501                 state.load(state.pop(), 0);
 502                 break;
 503             case ISTORE_1:
 504             case FSTORE_1:
 505             case ASTORE_1:
 506                 state.load(state.pop(), 1);
 507                 break;
 508             case ISTORE_2:
 509             case FSTORE_2:
 510             case ASTORE_2:
 511                 state.load(state.pop(), 2);
 512                 break;
 513             case ISTORE_3:
 514             case FSTORE_3:
 515             case ASTORE_3:
 516                 state.load(state.pop(), 3);
 517                 break;
 518             case ISTORE:
 519             case FSTORE:
 520             case ASTORE:
 521                 state.load(state.pop(), (int) optValue);
 522                 break;
 523             case LSTORE_0:
 524             case DSTORE_0:
 525                 state.load(state.pop2(), 0);
 526                 break;
 527             case LSTORE_1:
 528             case DSTORE_1:
 529                 state.load(state.pop2(), 1);
 530                 break;
 531             case LSTORE_2:
 532             case DSTORE_2:
 533                 state.load(state.pop2(), 2);
 534                 break;
 535             case LSTORE_3:
 536             case DSTORE_3:
 537                 state.load(state.pop2(), 3);
 538                 break;
 539             case LSTORE:
 540             case DSTORE:
 541                 state.load(state.pop2(), (int) optValue);
 542                 break;
 543             case POP:
 544             case LSHR:
 545             case LSHL:
 546             case LUSHR:
 547                 state.pop();
 548                 break;
 549             case ARETURN:
 550             case IRETURN:
 551             case FRETURN:
 552                 state.pop();
 553                 break;
 554             case ATHROW:
 555                 state.pop();
 556                 break;
 557             case POP2:
 558                 state.pop2();
 559                 break;
 560             case LRETURN:
 561             case DRETURN:
 562                 state.pop2();
 563                 break;
 564             case DUP:
 565                 state.push(state.peek());
 566                 break;
 567             case RETURN:
 568                 break;
 569             case ARRAYLENGTH:
 570                 state.pop();
 571                 state.push(TypeTag.I);
 572                 break;
 573             case ISUB:
 574             case IADD:
 575             case IMUL:
 576             case IDIV:
 577             case IREM:
 578             case ISHL:
 579             case ISHR:
 580             case IUSHR:
 581             case IAND:
 582             case IOR:
 583             case IXOR:
 584                 state.pop();
 585                 state.pop();
 586                 state.push(TypeTag.I);
 587                 break;
 588             case AASTORE:
 589                 state.pop();
 590                 state.pop();
 591                 state.pop();
 592                 break;
 593             case LAND:
 594             case LOR:
 595             case LXOR:
 596             case LREM:
 597             case LDIV:
 598             case LMUL:
 599             case LSUB:
 600             case LADD:
 601                 state.pop2();
 602                 state.pop2();
 603                 state.push(TypeTag.J);
 604                 break;
 605             case LCMP:
 606                 state.pop2();
 607                 state.pop2();
 608                 state.push(TypeTag.I);
 609                 break;
 610             case L2I:
 611                 state.pop2();
 612                 state.push(TypeTag.I);
 613                 break;
 614             case I2L:
 615                 state.pop();
 616                 state.push(TypeTag.J);
 617                 break;
 618             case I2F:
 619                 state.pop();
 620                 state.push(TypeTag.F);
 621                 break;
 622             case I2D:
 623                 state.pop();
 624                 state.push(TypeTag.D);
 625                 break;
 626             case L2F:
 627                 state.pop2();
 628                 state.push(TypeTag.F);
 629                 break;
 630             case L2D:
 631                 state.pop2();
 632                 state.push(TypeTag.D);
 633                 break;
 634             case F2I:
 635                 state.pop();
 636                 state.push(TypeTag.I);
 637                 break;
 638             case F2L:
 639                 state.pop();
 640                 state.push(TypeTag.J);
 641                 break;
 642             case F2D:
 643                 state.pop();
 644                 state.push(TypeTag.D);
 645                 break;
 646             case D2I:
 647                 state.pop2();
 648                 state.push(TypeTag.I);
 649                 break;
 650             case D2L:
 651                 state.pop2();
 652                 state.push(TypeTag.J);
 653                 break;
 654             case D2F:
 655                 state.pop2();
 656                 state.push(TypeTag.F);
 657                 break;
 658             case TABLESWITCH:
 659             case LOOKUPSWITCH:
 660                 state.pop();
 661                 break;
 662             case DUP_X1: {
 663                 T val1 = state.pop();
 664                 T val2 = state.pop();
 665                 state.push(val1);
 666                 state.push(val2);
 667                 state.push(val1);
 668                 break;
 669             }
 670             case BASTORE:
 671                 state.pop();
 672                 state.pop();
 673                 state.pop();
 674                 break;
 675             case I2B:
 676             case I2C:
 677             case I2S:
 678                 break;
 679             case FMUL:
 680             case FADD:
 681             case FSUB:
 682             case FDIV:
 683             case FREM:
 684                 state.pop();
 685                 state.pop();
 686                 state.push(TypeTag.F);
 687                 break;
 688             case CASTORE:
 689             case IASTORE:
 690             case FASTORE:
 691             case SASTORE:
 692                 state.pop();
 693                 state.pop();
 694                 state.pop();
 695                 break;
 696             case LASTORE:
 697             case DASTORE:
 698                 state.pop2();
 699                 state.pop();
 700                 state.pop();
 701                 break;
 702             case DUP2:
 703                 if (state.peek() != null) {
 704                     //form 1
 705                     T value1 = state.pop();
 706                     T value2 = state.pop();
 707                     state.push(value2);
 708                     state.push(value1);
 709                     state.push(value2);
 710                     state.push(value1);
 711                 } else {
 712                     //form 2
 713                     T value = state.pop2();
 714                     state.push(value);
 715                     state.push(value);
 716                 }
 717                 break;
 718             case DUP2_X1:
 719                 if (state.peek() != null) {
 720                     T value1 = state.pop();
 721                     T value2 = state.pop();
 722                     T value3 = state.pop();
 723                     state.push(value2);
 724                     state.push(value1);
 725                     state.push(value3);
 726                     state.push(value2);
 727                     state.push(value1);
 728                 } else {
 729                     T value1 = state.pop2();
 730                     T value2 = state.pop();
 731                     state.push(value1);
 732                     state.push(value2);
 733                     state.push(value1);
 734                 }
 735                 break;
 736             case DUP2_X2:
 737                 if (state.peek() != null) {
 738                     T value1 = state.pop();
 739                     T value2 = state.pop();
 740                     if (state.peek() != null) {
 741                         // form 1
 742                         T value3 = state.pop();
 743                         T value4 = state.pop();
 744                         state.push(value2);
 745                         state.push(value1);
 746                         state.push(value4);
 747                         state.push(value3);
 748                         state.push(value2);
 749                         state.push(value1);
 750                     } else {
 751                         // form 3
 752                         T value3 = state.pop2();
 753                         state.push(value2);
 754                         state.push(value1);
 755                         state.push(value3);
 756                         state.push(value2);
 757                         state.push(value1);
 758                     }
 759                 } else {
 760                     T value1 = state.pop2();
 761                     if (state.peek() != null) {
 762                         // form 2
 763                         T value2 = state.pop();
 764                         T value3 = state.pop();
 765                         state.push(value1);
 766                         state.push(value3);
 767                         state.push(value2);
 768                         state.push(value1);
 769                     } else {
 770                         // form 4
 771                         T value2 = state.pop2();
 772                         state.push(value1);
 773                         state.push(value2);
 774                         state.push(value1);
 775                     }
 776                 }
 777                 break;
 778             case DUP_X2: {
 779                 T value1 = state.pop();
 780                 if (state.peek() != null) {
 781                     // form 1
 782                     T value2 = state.pop();
 783                     T value3 = state.pop();
 784                     state.push(value1);
 785                     state.push(value3);
 786                     state.push(value2);
 787                     state.push(value1);
 788                 } else {
 789                     // form 2
 790                     T value2 = state.pop2();
 791                     state.push(value1);
 792                     state.push(value2);
 793                     state.push(value1);
 794                 }
 795             }
 796             break;
 797             case FCMPL:
 798             case FCMPG:
 799                 state.pop();
 800                 state.pop();
 801                 state.push(TypeTag.I);
 802                 break;
 803             case DCMPL:
 804             case DCMPG:
 805                 state.pop2();
 806                 state.pop2();
 807                 state.push(TypeTag.I);
 808                 break;
 809             case SWAP: {
 810                 T value1 = state.pop();
 811                 T value2 = state.pop();
 812                 state.push(value1);
 813                 state.push(value2);
 814                 break;
 815             }
 816             case DADD:
 817             case DSUB:
 818             case DMUL:
 819             case DDIV:
 820             case DREM:
 821                 state.pop2();
 822                 state.pop2();
 823                 state.push(TypeTag.D);
 824                 break;
 825             case RET:
 826                 break;
 827             case WIDE:
 828                 // must be handled by the caller.
 829                 return;
 830             case MONITORENTER:
 831             case MONITOREXIT:
 832                 state.pop();
 833                 break;
 834             case NEW:
 835             case DEFAULTVALUE:
 836                 state.push(typeHelper.type((S) optValue));
 837                 break;
 838             case NEWARRAY:
 839                 state.pop();
 840                 state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
 841                 break;
 842             case ANEWARRAY:
 843                 state.pop();
 844                 state.push(typeHelper.arrayOf(typeHelper.type((S)optValue)));
 845                 break;
 846             case MULTIANEWARRAY:
 847                 for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) {
 848                     state.pop();
 849                 }
 850                 state.push(typeHelper.type((S) ((Object[]) optValue)[0]));
 851                 break;
 852             case INVOKEINTERFACE:
 853             case INVOKEVIRTUAL:
 854             case INVOKESPECIAL:
 855             case INVOKESTATIC:
 856             case INVOKEDYNAMIC:
 857                 processInvoke(op, (T) optValue);
 858                 break;
 859             case GETSTATIC:
 860                 state.push((T) optValue);
 861                 break;
 862             case GETFIELD:
 863                 state.pop();
 864                 state.push((T) optValue);
 865                 break;
 866             case PUTSTATIC: {
 867                 TypeTag tag = typeHelper.tag((T) optValue);
 868                 if (tag.width == 1) {
 869                     state.pop();
 870                 } else {
 871                     state.pop2();
 872                 }
 873                 break;
 874             }
 875             case PUTFIELD: {
 876                 TypeTag tag = typeHelper.tag((T) optValue);
 877                 if (tag.width == 1) {
 878                     state.pop();
 879                 } else {
 880                     state.pop2();
 881                 }
 882                 state.pop();
 883                 break;
 884             }
 885             case WITHFIELD: {
 886                 TypeTag tag = typeHelper.tag((T) optValue);
 887                 if (tag.width == 1) {
 888                     state.pop();
 889                 } else {
 890                     state.pop2();
 891                 }
 892                 break;
 893             }
 894             case BIPUSH:
 895             case SIPUSH:
 896                 state.push(TypeTag.I);
 897                 break;
 898             case LDC:
 899             case LDC_W:
 900             case LDC2_W:
 901                 state.push((T)optValue);
 902                 break;
 903             case IF_ACMPEQ:
 904             case IF_ICMPEQ:
 905             case IF_ACMPNE:
 906             case IF_ICMPGE:
 907             case IF_ICMPGT:
 908             case IF_ICMPLE:
 909             case IF_ICMPLT:
 910             case IF_ICMPNE:
 911                 state.pop();
 912                 state.pop();
 913                 break;
 914             case IF_NONNULL:
 915             case IF_NULL:
 916             case IFEQ:
 917             case IFGE:
 918             case IFGT:
 919             case IFLE:
 920             case IFLT:
 921             case IFNE:
 922                 state.pop();
 923                 break;
 924             case INSTANCEOF:
 925                 state.pop();
 926                 state.push(TypeTag.Z);
 927                 break;
 928             case TYPED:
 929             case CHECKCAST:
 930                 break;
 931 
 932             default:
 933                 throw new UnsupportedOperationException("Unsupported opcode: " + op);
 934         }
 935     }
 936 
 937     void processInvoke(Opcode opcode, T invokedType) {
 938         Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType);
 939         while (paramsIt.hasNext()) {
 940             T t = paramsIt.next();
 941             TypeTag tag = typeHelper.tag(t);
 942             if (tag.width == 2) {
 943                 state.popInternal();
 944                 state.popInternal();
 945             } else {
 946                 state.popInternal();
 947             }
 948         }
 949         if (opcode != Opcode.INVOKESTATIC && opcode != Opcode.INVOKEDYNAMIC) {
 950             state.pop(); //receiver
 951         }
 952         T retType = typeHelper.returnType(invokedType);
 953         TypeTag retTag = typeHelper.tag(retType);
 954         if (retTag != TypeTag.V)
 955             state.push(retType);
 956     }
 957 
 958     @Override
 959     protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
 960         LdcPoolHelper ldcPoolHelper = new LdcPoolHelper();
 961         int index = indexFunc.applyAsInt(ldcPoolHelper);
 962         fat = typeHelper.tag(ldcPoolHelper.type).width() == 2;
 963         return super.ldc(index, ldcPoolHelper.type, fat);
 964     }
 965     //where
 966         class LdcPoolHelper implements PoolHelper<S, T, E> {
 967 
 968             T type;
 969 
 970             @Override
 971             public int putClass(S symbol) {
 972                 type = typeHelper.type(symbol);
 973                 return poolHelper.putClass(symbol);
 974             }
 975 
 976             @Override
 977             public int putValueClass(S symbol) {
 978                 throw new IllegalStateException();
 979             }
 980 
 981             @Override
 982             public int putInt(int i) {
 983                 type = typeHelper.fromTag(TypeTag.I);
 984                 return poolHelper.putInt(i);
 985             }
 986 
 987             @Override
 988             public int putFloat(float f) {
 989                 type = typeHelper.fromTag(TypeTag.F);
 990                 return poolHelper.putFloat(f);
 991             }
 992 
 993             @Override
 994             public int putLong(long l) {
 995                 type = typeHelper.fromTag(TypeTag.J);
 996                 return poolHelper.putLong(l);
 997             }
 998 
 999             @Override
1000             public int putDouble(double d) {
1001                 type = typeHelper.fromTag(TypeTag.D);
1002                 return poolHelper.putDouble(d);
1003             }
1004 
1005             @Override
1006             public int putString(String s) {
1007                 type = typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
1008                 return poolHelper.putString(s);
1009             }
1010 
1011             @Override
1012             public int putValue(Object v) {
1013                 type = typeTag(v);
1014                 return poolHelper.putValue(v);
1015             }
1016 
1017             @Override
1018             public int putConstantDynamic(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
1019                 type = constType;
1020                 return poolHelper.putConstantDynamic(constName, constType, bsmClass, bsmName, bsmType, staticArgs);
1021             }
1022 
1023             @Override
1024             public int putFieldRef(S owner, CharSequence name, T type) {
1025                 throw new IllegalStateException();
1026             }
1027 
1028             @Override
1029             public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
1030                 throw new IllegalStateException();
1031             }
1032 
1033             @Override
1034             public int putUtf8(CharSequence s) {
1035                 throw new IllegalStateException();
1036             }
1037 
1038             @Override
1039             public int putType(T t) {
1040                 throw new IllegalStateException();
1041             }
1042 
1043             @Override
1044             public int putMethodType(T t) {
1045                 type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
1046                 return poolHelper.putMethodType(t);
1047             }
1048 
1049             @Override
1050             public int putMethodHandle(int refKind, S owner, CharSequence name, T t) {
1051                 type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
1052                 return poolHelper.putMethodHandle(refKind, owner, name, t);
1053             }
1054 
1055             @Override
1056             public int putMethodHandle(int refKind, S owner, CharSequence name, T t, boolean isInterface) {
1057                 type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
1058                 return poolHelper.putMethodHandle(refKind, owner, name, t, isInterface);
1059             }
1060 
1061             @Override
1062             public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
1063                 throw new IllegalStateException();
1064             }
1065 
1066             @Override
1067             public int size() {
1068                 throw new IllegalStateException();
1069             }
1070 
1071             @Override
1072             public E representation() {
1073                 throw new IllegalStateException();
1074             }
1075 
1076             private T typeTag(Object o) {
1077                 if (o instanceof Double) {
1078                     return typeHelper.fromTag(TypeTag.D);
1079                 } else if (o instanceof Long) {
1080                     return typeHelper.fromTag(TypeTag.J);
1081                 } else if (o instanceof Float) {
1082                     return typeHelper.fromTag(TypeTag.F);
1083                 } else if (o instanceof Integer) {
1084                     return typeHelper.fromTag(TypeTag.I);
1085                 } else if (o instanceof Class<?>) {
1086                     return typeHelper.type(typeHelper.symbolFrom("java/lang/Class"));
1087                 } else if (o instanceof String) {
1088                     return typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
1089                 } else if (o instanceof MethodHandle) {
1090                     return typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
1091                 } else if (o instanceof MethodType) {
1092                     return typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
1093                 } else {
1094                     throw new IllegalStateException("Unsupported object class: " + o.getClass().getName());
1095                 }
1096             }
1097     }
1098 
1099     public C load(int index) {
1100         return load(typeHelper.tag(state.locals.get(index)), index);
1101     }
1102 
1103     public C store(int index) {
1104         return store(typeHelper.tag(state.tosType()), index);
1105     }
1106 
1107     @Override
1108     public C withLocalSize(int localsize) {
1109         throw new IllegalStateException("Local size automatically computed");
1110     }
1111 
1112     @Override
1113     public C withStackSize(int stacksize) {
1114         throw new IllegalStateException("Stack size automatically computed");
1115     }
1116 
1117     public C withLocal(CharSequence name, T type) {
1118         int offset = currLocalOffset;
1119         TypeTag tag = typeHelper.tag(type);
1120         lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
1121         state.load(type, offset);
1122         currLocalOffset += tag.width;
1123         return thisBuilder();
1124     }
1125 
1126     public C load(CharSequence local) {
1127         return load(lvarOffsets.get(local).offset);
1128     }
1129 
1130     public C store(CharSequence local) {
1131         return store(lvarOffsets.get(local).offset);
1132     }
1133 
1134     @Override
1135     public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
1136         return super.withTry(c -> {
1137             withLocalScope(() -> {
1138                 tryBlock.accept(c);
1139             });
1140         }, catchBlocks);
1141     }
1142 
1143     @Override
1144     protected CatchBuilder makeCatchBuilder(int start, int end) {
1145         return new TypedCatchBuilder(start, end);
1146     }
1147 
1148     class TypedCatchBuilder extends CatchBuilder {
1149 
1150         State initialState = state.dup();
1151 
1152         TypedCatchBuilder(int start, int end) {
1153             super(start, end);
1154         }
1155 
1156         @Override
1157         protected void emitCatch(S exc, Consumer<? super C> catcher) {
1158             withLocalScope(() -> {
1159                 state.push(typeHelper.type(exc));
1160                 emitStackMap(code.offset);
1161                 super.emitCatch(exc, catcher);
1162                 state = initialState;
1163             });
1164         }
1165 
1166         @Override
1167         protected void emitFinalizer() {
1168             withLocalScope(() -> {
1169                 state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
1170                 emitStackMap(code.offset);
1171                 super.emitFinalizer();
1172             });
1173         }
1174     }
1175 
1176     protected void withLocalScope(Runnable runnable) {
1177         int prevDepth = depth;
1178         try {
1179             depth++;
1180             runnable.run();
1181         } finally {
1182             Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
1183             while (lvarIt.hasNext()) {
1184                 LocalVarInfo lvi = lvarIt.next().getValue();
1185                 if (lvi.depth == depth) {
1186                     int width = lvi.type.width;
1187                     currLocalOffset -= width;
1188                     lvarIt.remove();
1189                 }
1190             }
1191             depth = prevDepth;
1192         }
1193     }
1194 
1195     @Override
1196     void addPendingJump(CharSequence label, int pc) {
1197         pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
1198     }
1199 
1200     @Override
1201     void resolveJumps(CharSequence label, int pc) {
1202         super.resolveJumps(label, pc);
1203         emitStackMap(pc);
1204     }
1205 
1206     //TODO: optimize stackmap generation by avoiding intermediate classes
1207     protected void emitStackMap(int pc) {
1208         //stack map generation
1209         if (pc > lastStackMapPc) {
1210             writeStackMapFrame(pc);
1211             lastStackMapState = state.dup();
1212             lastStackMapPc = pc;
1213             nstackmaps++;
1214         }
1215     }
1216 
1217     @Override
1218     void build(GrowableByteBuffer buf) {
1219         if (stacksize == -1) {
1220             throw new IllegalStateException("Bad stack size");
1221         }
1222         if (localsize == -1) {
1223             throw new IllegalStateException("Bad locals size");
1224         }
1225         if (nstackmaps > 0) {
1226             GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
1227             stackmapsAttr.writeChar(nstackmaps);
1228             stackmapsAttr.writeBytes(stackmaps);
1229             withAttribute("StackMapTable", stackmapsAttr.bytes());
1230         }
1231         super.build(buf);
1232     }
1233 
1234     /**
1235      * Compare this frame with the previous frame and produce
1236      * an entry of compressed stack map frame.
1237      */
1238     void writeStackMapFrame(int pc) {
1239         List<T> locals = state.locals;
1240         List<T> stack = state.stack;
1241         List<T> prev_locals = lastStackMapState.locals;
1242         int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
1243         if (stack.size() == 1) {
1244             if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
1245                 sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
1246                 return;
1247             }
1248         } else if (stack.size() == 0) {
1249             int diff_length = prev_locals.size() - locals.size();
1250             if (diff_length == 0) {
1251                 sameFrame(offset_delta);
1252                 return;
1253             } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
1254                 appendFrame(offset_delta, prev_locals.size(), locals);
1255                 return;
1256             } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
1257                 chopFrame(offset_delta, diff_length);
1258                 return;
1259             }
1260         }
1261         fullFrame(offset_delta, locals, stack);
1262     }
1263 }