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