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