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 putHandle(int refKind, S owner, CharSequence name, T t, boolean isInterface) {
1037                 type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
1038                 return poolHelper.putHandle(refKind, owner, name, t, isInterface);
1039             }
1040 
1041             @Override
1042             public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
1043                 throw new IllegalStateException();
1044             }
1045 
1046             @Override
1047             public int size() {
1048                 throw new IllegalStateException();
1049             }
1050 
1051             @Override
1052             public E entries() {
1053                 throw new IllegalStateException();
1054             }
1055     }
1056 
1057     public C load(int index) {
1058         return load(typeHelper.tag(state.locals.get(index)), index);
1059     }
1060 
1061     public C store(int index) {
1062         return store(typeHelper.tag(state.tosType()), index);
1063     }
1064 
1065     @Override
1066     public C withLocalSize(int localsize) {
1067         throw new IllegalStateException("Local size automatically computed");
1068     }
1069 
1070     @Override
1071     public C withStackSize(int stacksize) {
1072         throw new IllegalStateException("Stack size automatically computed");
1073     }
1074 
1075     public C withLocal(CharSequence name, T type) {
1076         int offset = currLocalOffset;
1077         TypeTag tag = typeHelper.tag(type);
1078         lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
1079         state.load(type, offset);
1080         currLocalOffset += tag.width;
1081         return thisBuilder();
1082     }
1083 
1084     public C load(CharSequence local) {
1085         return load(lvarOffsets.get(local).offset);
1086     }
1087 
1088     public C store(CharSequence local) {
1089         return store(lvarOffsets.get(local).offset);
1090     }
1091 
1092     @Override
1093     public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
1094         return super.withTry(c -> {
1095             withLocalScope(() -> {
1096                 tryBlock.accept(c);
1097             });
1098         }, catchBlocks);
1099     }
1100 
1101     @Override
1102     protected CatchBuilder makeCatchBuilder(int start, int end) {
1103         return new TypedCatchBuilder(start, end);
1104     }
1105 
1106     class TypedCatchBuilder extends CatchBuilder {
1107 
1108         State initialState = state.dup();
1109 
1110         TypedCatchBuilder(int start, int end) {
1111             super(start, end);
1112         }
1113 
1114         @Override
1115         protected void emitCatch(S exc, Consumer<? super C> catcher) {
1116             withLocalScope(() -> {
1117                 state.push(typeHelper.type(exc));
1118                 emitStackMap(code.offset);
1119                 super.emitCatch(exc, catcher);
1120                 state = initialState;
1121             });
1122         }
1123 
1124         @Override
1125         protected void emitFinalizer() {
1126             withLocalScope(() -> {
1127                 state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
1128                 emitStackMap(code.offset);
1129                 super.emitFinalizer();
1130             });
1131         }
1132     }
1133 
1134     protected void withLocalScope(Runnable runnable) {
1135         int prevDepth = depth;
1136         try {
1137             depth++;
1138             runnable.run();
1139         } finally {
1140             Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
1141             while (lvarIt.hasNext()) {
1142                 LocalVarInfo lvi = lvarIt.next().getValue();
1143                 if (lvi.depth == depth) {
1144                     int width = lvi.type.width;
1145                     currLocalOffset -= width;
1146                     lvarIt.remove();
1147                 }
1148             }
1149             depth = prevDepth;
1150         }
1151     }
1152 
1153     @Override
1154     void addPendingJump(CharSequence label, int pc) {
1155         pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
1156     }
1157 
1158     @Override
1159     void resolveJumps(CharSequence label, int pc) {
1160         super.resolveJumps(label, pc);
1161         emitStackMap(pc);
1162     }
1163 
1164     //TODO: optimize stackmap generation by avoiding intermediate classes
1165     protected void emitStackMap(int pc) {
1166         //stack map generation
1167         if (pc > lastStackMapPc) {
1168             writeStackMapFrame(pc);
1169             lastStackMapState = state.dup();
1170             lastStackMapPc = pc;
1171             nstackmaps++;
1172         }
1173     }
1174 
1175     @Override
1176     void build(GrowableByteBuffer buf) {
1177         if (stacksize == -1) {
1178             throw new IllegalStateException("Bad stack size");
1179         }
1180         if (localsize == -1) {
1181             throw new IllegalStateException("Bad locals size");
1182         }
1183         if (nstackmaps > 0) {
1184             GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
1185             stackmapsAttr.writeChar(nstackmaps);
1186             stackmapsAttr.writeBytes(stackmaps);
1187             withAttribute("StackMapTable", stackmapsAttr.bytes());
1188         }
1189         super.build(buf);
1190     }
1191 
1192     /**
1193      * Compare this frame with the previous frame and produce
1194      * an entry of compressed stack map frame.
1195      */
1196     void writeStackMapFrame(int pc) {
1197         List<T> locals = state.locals;
1198         List<T> stack = state.stack;
1199         List<T> prev_locals = lastStackMapState.locals;
1200         int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
1201         if (stack.size() == 1) {
1202             if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
1203                 sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
1204                 return;
1205             }
1206         } else if (stack.size() == 0) {
1207             int diff_length = prev_locals.size() - locals.size();
1208             if (diff_length == 0) {
1209                 sameFrame(offset_delta);
1210                 return;
1211             } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
1212                 appendFrame(offset_delta, prev_locals.size(), locals);
1213                 return;
1214             } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
1215                 chopFrame(offset_delta, diff_length);
1216                 return;
1217             }
1218         }
1219         fullFrame(offset_delta, locals, stack);
1220     }
1221 }