1 /*
   2  * Copyright (c) 2016, 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.test.lib.jittester.visitors;
  25 
  26 import java.util.ArrayDeque;
  27 import java.util.Deque;
  28 import java.util.HashMap;
  29 import java.util.List;
  30 import java.util.NoSuchElementException;
  31 import java.util.Objects;
  32 import java.util.SortedMap;
  33 import java.util.TreeMap;
  34 import java.util.stream.Collectors;
  35 import java.util.stream.Stream;
  36 
  37 import jdk.internal.org.objectweb.asm.ClassWriter;
  38 import jdk.internal.org.objectweb.asm.FieldVisitor;
  39 import jdk.internal.org.objectweb.asm.Label;
  40 import jdk.internal.org.objectweb.asm.MethodVisitor;
  41 import jdk.internal.org.objectweb.asm.Opcodes;
  42 import jdk.test.lib.Pair;
  43 import jdk.test.lib.jittester.BinaryOperator;
  44 import jdk.test.lib.jittester.Block;
  45 import jdk.test.lib.jittester.BuiltInType;
  46 import jdk.test.lib.jittester.Break;
  47 import jdk.test.lib.jittester.CastOperator;
  48 import jdk.test.lib.jittester.CatchBlock;
  49 import jdk.test.lib.jittester.Continue;
  50 import jdk.test.lib.jittester.Declaration;
  51 import jdk.test.lib.jittester.IRNode;
  52 import jdk.test.lib.jittester.If;
  53 import jdk.test.lib.jittester.Initialization;
  54 import jdk.test.lib.jittester.Literal;
  55 import jdk.test.lib.jittester.LocalVariable;
  56 import jdk.test.lib.jittester.NonStaticMemberVariable;
  57 import jdk.test.lib.jittester.Nothing;
  58 import jdk.test.lib.jittester.Operator;
  59 import jdk.test.lib.jittester.OperatorKind;
  60 import jdk.test.lib.jittester.PrintVariables;
  61 import jdk.test.lib.jittester.ProductionParams;
  62 import jdk.test.lib.jittester.Statement;
  63 import jdk.test.lib.jittester.StaticMemberVariable;
  64 import jdk.test.lib.jittester.Switch;
  65 import jdk.test.lib.jittester.Symbol;
  66 import jdk.test.lib.jittester.TernaryOperator;
  67 import jdk.test.lib.jittester.Throw;
  68 import jdk.test.lib.jittester.TryCatchBlock;
  69 import jdk.test.lib.jittester.Type;
  70 import jdk.test.lib.jittester.TypeList;
  71 import jdk.test.lib.jittester.UnaryOperator;
  72 import jdk.test.lib.jittester.VariableBase;
  73 import jdk.test.lib.jittester.VariableDeclaration;
  74 import jdk.test.lib.jittester.VariableDeclarationBlock;
  75 import jdk.test.lib.jittester.VariableInfo;
  76 import jdk.test.lib.jittester.VariableInitialization;
  77 import jdk.test.lib.jittester.arrays.ArrayCreation;
  78 import jdk.test.lib.jittester.arrays.ArrayElement;
  79 import jdk.test.lib.jittester.arrays.ArrayExtraction;
  80 import jdk.test.lib.jittester.classes.ClassDefinitionBlock;
  81 import jdk.test.lib.jittester.classes.Interface;
  82 import jdk.test.lib.jittester.classes.Klass;
  83 import jdk.test.lib.jittester.classes.MainKlass;
  84 import jdk.test.lib.jittester.functions.ArgumentDeclaration;
  85 import jdk.test.lib.jittester.functions.ConstructorDefinition;
  86 import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock;
  87 import jdk.test.lib.jittester.functions.Function;
  88 import jdk.test.lib.jittester.functions.FunctionDeclaration;
  89 import jdk.test.lib.jittester.functions.FunctionDeclarationBlock;
  90 import jdk.test.lib.jittester.functions.FunctionDefinition;
  91 import jdk.test.lib.jittester.functions.FunctionDefinitionBlock;
  92 import jdk.test.lib.jittester.functions.FunctionInfo;
  93 import jdk.test.lib.jittester.functions.FunctionRedefinition;
  94 import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock;
  95 import jdk.test.lib.jittester.functions.Return;
  96 import jdk.test.lib.jittester.functions.StaticConstructorDefinition;
  97 import jdk.test.lib.jittester.loops.CounterInitializer;
  98 import jdk.test.lib.jittester.loops.CounterManipulator;
  99 import jdk.test.lib.jittester.loops.DoWhile;
 100 import jdk.test.lib.jittester.loops.For;
 101 import jdk.test.lib.jittester.loops.Loop;
 102 import jdk.test.lib.jittester.loops.LoopingCondition;
 103 import jdk.test.lib.jittester.loops.While;
 104 import jdk.test.lib.jittester.types.TypeArray;
 105 import jdk.test.lib.jittester.types.TypeKlass;
 106 import jdk.test.lib.jittester.utils.FixedTrees;
 107 import jdk.test.lib.jittester.utils.PseudoRandom;
 108 
 109 public class ByteCodeVisitor implements Visitor<byte[]> {
 110     private final GeneratedClassesContext context = new GeneratedClassesContext();
 111     private final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 112     private final int CLASS_WRITER_FLAGS = ContextDependedClassWriter.COMPUTE_MAXS | ContextDependedClassWriter.COMPUTE_FRAMES;
 113     private final HashMap<String, ContextDependedClassWriter> classWriters = new HashMap<>();
 114     private MethodVisitor currentMV;
 115     private TypeKlass currentClass;
 116     private final LocalVariablesTable locals = new LocalVariablesTable();
 117     private final Deque<Label> endLabels = new ArrayDeque<>();
 118     private final Deque<Label> beginLabels = new ArrayDeque<>();
 119 
 120     @Override
 121     public byte[] visit(ArgumentDeclaration node) {
 122         /* handled by FunctionDefinition for ByteCodeVisitor */
 123         return EMPTY_BYTE_ARRAY;
 124     }
 125 
 126     @Override
 127     public byte[] visit(ArrayCreation node) {
 128         int dimensions = node.getDimensionsCount();
 129         TypeArray arrayType = node.getArrayType();
 130         Type basicType = arrayType.type;
 131         for (IRNode child : node.getChildren()) {
 132             child.accept(this);
 133         }
 134         if (dimensions == 1) {
 135             if (basicType.equals(TypeList.BOOLEAN)) {
 136                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BOOLEAN);
 137             } else if (basicType.equals(TypeList.BYTE)) {
 138                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE);
 139             } else if (basicType.equals(TypeList.CHAR)) {
 140                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_CHAR);
 141             } else if (basicType.equals(TypeList.SHORT)) {
 142                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_SHORT);
 143             } else if (basicType.equals(TypeList.INT)) {
 144                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
 145             } else if (basicType.equals(TypeList.LONG)) {
 146                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_LONG);
 147             } else if (basicType.equals(TypeList.FLOAT)) {
 148                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_FLOAT);
 149             } else if (basicType.equals(TypeList.DOUBLE)) {
 150                 currentMV.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_DOUBLE);
 151             } else {
 152                 currentMV.visitTypeInsn(Opcodes.ANEWARRAY, asInternalName(basicType.getName()));
 153             }
 154         } else {
 155             currentMV.visitMultiANewArrayInsn(new String(arrayType.accept(this)), dimensions);
 156         }
 157         return EMPTY_BYTE_ARRAY;
 158     }
 159 
 160     @Override
 161     public byte[] visit(ArrayElement node) {
 162         node.getChild(0).accept(this);
 163         int dimensions = node.getChildren().size() - 1;
 164         Type resultType = node.getResultType();
 165         for (int i = 1; i < dimensions; i++) {
 166             node.getChild(i).accept(this);
 167             currentMV.visitInsn(Opcodes.AALOAD);
 168         }
 169         node.getChild(dimensions).accept(this);
 170         if (resultType.equals(TypeList.BOOLEAN) || resultType.equals(TypeList.BYTE)) {
 171             currentMV.visitInsn(Opcodes.BALOAD);
 172         } else if (resultType.equals(TypeList.CHAR)) {
 173             currentMV.visitInsn(Opcodes.CALOAD);
 174         } else if (resultType.equals(TypeList.SHORT)) {
 175             currentMV.visitInsn(Opcodes.SALOAD);
 176         } else if (resultType.equals(TypeList.INT)) {
 177             currentMV.visitInsn(Opcodes.IALOAD);
 178         } else if (resultType.equals(TypeList.LONG)) {
 179             currentMV.visitInsn(Opcodes.LALOAD);
 180         } else if (resultType.equals(TypeList.FLOAT)) {
 181             currentMV.visitInsn(Opcodes.FALOAD);
 182         } else if (resultType.equals(TypeList.DOUBLE)) {
 183             currentMV.visitInsn(Opcodes.DALOAD);
 184         } else {
 185             currentMV.visitInsn(Opcodes.AALOAD);
 186         }
 187         return EMPTY_BYTE_ARRAY;
 188     }
 189 
 190     @Override
 191     public byte[] visit(ArrayExtraction node) {
 192         node.getChild(0).accept(this);
 193         int dimensions = node.getChildren().size() - 1;
 194         Type resultType = node.getResultType();
 195         for (int i = 1; i < dimensions; i++) {
 196             node.getChild(i).accept(this);
 197             currentMV.visitInsn(Opcodes.AALOAD);
 198         }
 199         node.getChild(dimensions).accept(this);
 200         if (resultType.equals(TypeList.BOOLEAN) || resultType.equals(TypeList.BYTE)) {
 201             currentMV.visitInsn(Opcodes.BALOAD);
 202         } else if (resultType.equals(TypeList.CHAR)) {
 203             currentMV.visitInsn(Opcodes.CALOAD);
 204         } else if (resultType.equals(TypeList.SHORT)) {
 205             currentMV.visitInsn(Opcodes.SALOAD);
 206         } else if (resultType.equals(TypeList.INT)) {
 207             currentMV.visitInsn(Opcodes.IALOAD);
 208         } else if (resultType.equals(TypeList.LONG)) {
 209             currentMV.visitInsn(Opcodes.LALOAD);
 210         } else if (resultType.equals(TypeList.FLOAT)) {
 211             currentMV.visitInsn(Opcodes.FALOAD);
 212         } else if (resultType.equals(TypeList.DOUBLE)) {
 213             currentMV.visitInsn(Opcodes.DALOAD);
 214         } else {
 215             currentMV.visitInsn(Opcodes.AALOAD);
 216         }
 217         return EMPTY_BYTE_ARRAY;
 218     }
 219 
 220     @Override
 221     public byte[] visit(BinaryOperator node) {
 222         OperatorKind kind = node.getOperationKind();
 223         IRNode left = node.getChild(Operator.Order.LEFT.ordinal());
 224         IRNode right = node.getChild(Operator.Order.RIGHT.ordinal());
 225         Type resultType = node.getResultType();
 226         if (left == null || right == null) {
 227             return EMPTY_BYTE_ARRAY;
 228         }
 229         boolean needTypeConversion = false;
 230         boolean convertRightArg = false;
 231         Type leftType = left.getResultType();
 232         Type rightType = right.getResultType();
 233         if (!leftType.equals(rightType) && leftType instanceof BuiltInType
 234                 && rightType instanceof BuiltInType
 235                 && kind != OperatorKind.SAR && kind != OperatorKind.SHL
 236                 && kind != OperatorKind.SHR && kind != OperatorKind.ASSIGN
 237                 && kind != OperatorKind.AND && kind != OperatorKind.OR) {
 238             needTypeConversion = true;
 239             BuiltInType leftBuiltIn = (BuiltInType) leftType;
 240             BuiltInType rightBuiltIn = (BuiltInType) rightType;
 241             convertRightArg = leftBuiltIn.isMoreCapaciousThan(rightBuiltIn);
 242         }
 243         Type mostCapacious = convertRightArg ? leftType : rightType;
 244         if (!rightType.equals(TypeList.INT)
 245                 && (kind == OperatorKind.SHL || kind == OperatorKind.SHR
 246                 || kind == OperatorKind.SAR)) {
 247             left.accept(this);
 248             right.accept(this);
 249             convertTopType(rightType, TypeList.INT);
 250         } else if (kind != OperatorKind.ASSIGN && kind != OperatorKind.OR
 251                 && kind != OperatorKind.AND && kind != OperatorKind.COMPOUND_ADD
 252                 && kind != OperatorKind.COMPOUND_AND && kind != OperatorKind.COMPOUND_DIV
 253                 && kind != OperatorKind.COMPOUND_MOD && kind != OperatorKind.COMPOUND_MUL
 254                 && kind != OperatorKind.COMPOUND_OR && kind != OperatorKind.COMPOUND_SAR
 255                 && kind != OperatorKind.COMPOUND_SHL && kind != OperatorKind.COMPOUND_SHR
 256                 && kind != OperatorKind.COMPOUND_SUB && kind != OperatorKind.COMPOUND_XOR
 257                 && kind != OperatorKind.STRADD) {
 258                 /* "assign", "and", "or", concat and all compound operators are
 259                     handled differently and shouldn't just place left and right
 260                     operands on stack */
 261             left.accept(this);
 262             if (needTypeConversion && !convertRightArg) {
 263                 convertTopType(leftType, rightType);
 264             }
 265             right.accept(this);
 266             if (needTypeConversion && convertRightArg) {
 267                 convertTopType(rightType, leftType);
 268             }
 269         }
 270         switch (kind) {
 271             case ASSIGN:
 272                 VariableInfo vi = ((VariableBase)left).getVariableInfo();
 273                 Type varType = vi.type;
 274                 if (left instanceof LocalVariable) {
 275                     right.accept(this);
 276                     convertTopType(rightType, leftType);
 277                     int index = locals.getLocalIndex(vi);
 278                     if (varType.equals(TypeList.LONG)) {
 279                         currentMV.visitVarInsn(Opcodes.LSTORE, index);
 280                         currentMV.visitVarInsn(Opcodes.LLOAD, index);
 281                     } else if (varType.equals(TypeList.DOUBLE)) {
 282                         currentMV.visitVarInsn(Opcodes.DSTORE, index);
 283                         currentMV.visitVarInsn(Opcodes.DLOAD, index);
 284                     } else if (varType.equals(TypeList.FLOAT)) {
 285                         currentMV.visitVarInsn(Opcodes.FSTORE, index);
 286                         currentMV.visitVarInsn(Opcodes.FLOAD, index);
 287                     } else if (varType instanceof TypeKlass) {
 288                         currentMV.visitVarInsn(Opcodes.ASTORE, index);
 289                         currentMV.visitVarInsn(Opcodes.ALOAD, index);
 290                     } else {
 291                         currentMV.visitVarInsn(Opcodes.ISTORE, index);
 292                         currentMV.visitVarInsn(Opcodes.ILOAD, index);
 293                     }
 294                 } else if (left instanceof StaticMemberVariable) {
 295                     right.accept(this);
 296                     convertTopType(rightType, leftType);
 297                     String typeDescr = new String(vi.type.accept(this));
 298                     String ownerName = asInternalName(vi.getOwner().getName());
 299                     currentMV.visitFieldInsn(Opcodes.PUTSTATIC, ownerName,
 300                             vi.name, typeDescr);
 301                     currentMV.visitFieldInsn(Opcodes.GETSTATIC, ownerName,
 302                             vi.name, typeDescr);
 303                 } else if (left instanceof NonStaticMemberVariable) {
 304                     // put object to stack for putfield
 305                     left.getChild(0).accept(this);
 306                     // put object to stack for getfield
 307                     currentMV.visitInsn(Opcodes.DUP);
 308                     right.accept(this);
 309                     convertTopType(rightType, leftType);
 310                     String typeDescr = new String(vi.type.accept(this));
 311                     String ownerName = asInternalName(vi.getOwner().getName());
 312                     currentMV.visitFieldInsn(Opcodes.PUTFIELD, ownerName,
 313                             vi.name, typeDescr);
 314                     currentMV.visitFieldInsn(Opcodes.GETFIELD, ownerName,
 315                             vi.name, typeDescr);
 316                 } else {
 317                     throw new IllegalArgumentException("illegal left operand : "
 318                             + left + "("+left.getClass()+")");
 319                 }
 320                 break;
 321             case OR:
 322                 generateBasicLogicOperator(Opcodes.IFNE, false, left, right);
 323                 break;
 324             case AND:
 325                 generateBasicLogicOperator(Opcodes.IFEQ, true,  left, right);
 326                 break;
 327             case BIT_OR:
 328                 if (mostCapacious.equals(TypeList.LONG)) {
 329                     currentMV.visitInsn(Opcodes.LOR);
 330                 } else {
 331                     currentMV.visitInsn(Opcodes.IOR);
 332                 }
 333                 break;
 334             case BIT_XOR:
 335                 if (mostCapacious.equals(TypeList.LONG)) {
 336                     currentMV.visitInsn(Opcodes.LXOR);
 337                 } else {
 338                     currentMV.visitInsn(Opcodes.IXOR);
 339                 }
 340                 break;
 341             case BIT_AND:
 342                 if (mostCapacious.equals(TypeList.LONG)) {
 343                     currentMV.visitInsn(Opcodes.LAND);
 344                 } else {
 345                     currentMV.visitInsn(Opcodes.IAND);
 346                 }
 347                 break;
 348             case EQ:
 349                 generateCmpBasedCode(mostCapacious, Opcodes.IFEQ, Opcodes.IF_ICMPEQ);
 350                 break;
 351             case NE:
 352                 generateCmpBasedCode(mostCapacious, Opcodes.IFNE, Opcodes.IF_ICMPNE);
 353                 break;
 354             case GT:
 355                 generateCmpBasedCode(mostCapacious, Opcodes.IFGT, Opcodes.IF_ICMPGT);
 356                 break;
 357             case LT:
 358                 generateCmpBasedCode(mostCapacious, Opcodes.IFLT, Opcodes.IF_ICMPLT);
 359                 break;
 360             case GE:
 361                 generateCmpBasedCode(mostCapacious, Opcodes.IFGE, Opcodes.IF_ICMPGE);
 362                 break;
 363             case LE:
 364                 generateCmpBasedCode(mostCapacious, Opcodes.IFLE, Opcodes.IF_ICMPLE);
 365                 break;
 366             case SHR:
 367                 if (leftType.equals(TypeList.LONG)) {
 368                     currentMV.visitInsn(Opcodes.LSHR);
 369                 } else {
 370                     currentMV.visitInsn(Opcodes.ISHR);
 371                 }
 372                 break;
 373             case SHL:
 374                 if (leftType.equals(TypeList.LONG)) {
 375                     currentMV.visitInsn(Opcodes.LSHL);
 376                 } else {
 377                     currentMV.visitInsn(Opcodes.ISHL);
 378                 }
 379                 break;
 380             case SAR:
 381                 if (leftType.equals(TypeList.LONG)) {
 382                     currentMV.visitInsn(Opcodes.LUSHR);
 383                 } else {
 384                     currentMV.visitInsn(Opcodes.IUSHR);
 385                 }
 386                 break;
 387             case STRADD:
 388                 // we use String::valueOf to change null to "null"
 389                 left.accept(this);
 390                 currentMV.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/String", "valueOf",
 391                         "(Ljava/lang/Object;)Ljava/lang/String;", false /* not interface */);
 392                 right.accept(this);
 393                 currentMV.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/String", "valueOf",
 394                         "(Ljava/lang/Object;)Ljava/lang/String;", false /* not interface */);
 395                 currentMV.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "concat",
 396                         "(Ljava/lang/String;)Ljava/lang/String;", false /* not interface */);
 397                 break;
 398             case ADD:
 399                 if (mostCapacious.equals(TypeList.LONG)) {
 400                     currentMV.visitInsn(Opcodes.LADD);
 401                 } else if (mostCapacious.equals(TypeList.DOUBLE)) {
 402                     currentMV.visitInsn(Opcodes.DADD);
 403                 } else if (mostCapacious.equals(TypeList.FLOAT)) {
 404                     currentMV.visitInsn(Opcodes.FADD);
 405                 } else {
 406                     currentMV.visitInsn(Opcodes.IADD);
 407                 }
 408                 break;
 409             case SUB:
 410                 if (mostCapacious.equals(TypeList.LONG)) {
 411                     currentMV.visitInsn(Opcodes.LSUB);
 412                 } else if (mostCapacious.equals(TypeList.DOUBLE)) {
 413                     currentMV.visitInsn(Opcodes.DSUB);
 414                 } else if (mostCapacious.equals(TypeList.FLOAT)) {
 415                     currentMV.visitInsn(Opcodes.FSUB);
 416                 } else {
 417                     currentMV.visitInsn(Opcodes.ISUB);
 418                 }
 419                 break;
 420             case MUL:
 421                 if (mostCapacious.equals(TypeList.LONG)) {
 422                     currentMV.visitInsn(Opcodes.LMUL);
 423                 } else if (mostCapacious.equals(TypeList.DOUBLE)) {
 424                     currentMV.visitInsn(Opcodes.DMUL);
 425                 } else if (mostCapacious.equals(TypeList.FLOAT)) {
 426                     currentMV.visitInsn(Opcodes.FMUL);
 427                 } else {
 428                     currentMV.visitInsn(Opcodes.IMUL);
 429                 }
 430                 break;
 431             case DIV:
 432                 if (mostCapacious.equals(TypeList.LONG)) {
 433                     currentMV.visitInsn(Opcodes.LDIV);
 434                 } else if (mostCapacious.equals(TypeList.DOUBLE)) {
 435                     currentMV.visitInsn(Opcodes.DDIV);
 436                 } else if (mostCapacious.equals(TypeList.FLOAT)) {
 437                     currentMV.visitInsn(Opcodes.FDIV);
 438                 } else {
 439                     currentMV.visitInsn(Opcodes.IDIV);
 440                 }
 441                 break;
 442             case MOD:
 443                 if (mostCapacious.equals(TypeList.LONG)) {
 444                     currentMV.visitInsn(Opcodes.LREM);
 445                 } else if (mostCapacious.equals(TypeList.DOUBLE)) {
 446                     currentMV.visitInsn(Opcodes.DREM);
 447                 } else if (mostCapacious.equals(TypeList.FLOAT)) {
 448                     currentMV.visitInsn(Opcodes.FREM);
 449                 } else {
 450                     currentMV.visitInsn(Opcodes.IREM);
 451                 }
 452                 break;
 453             case COMPOUND_ADD:
 454                 lowerCompoundBinaryOperator("java.lang.String".equals(leftType.getName())
 455                         ? OperatorKind.STRADD : OperatorKind.ADD, node);
 456                 break;
 457             case COMPOUND_SUB:
 458                 lowerCompoundBinaryOperator(OperatorKind.SUB, node);
 459                 break;
 460             case COMPOUND_MUL:
 461                 lowerCompoundBinaryOperator(OperatorKind.MUL, node);
 462                 break;
 463             case COMPOUND_DIV:
 464                 lowerCompoundBinaryOperator(OperatorKind.DIV, node);
 465                 break;
 466             case COMPOUND_MOD:
 467                 lowerCompoundBinaryOperator(OperatorKind.MOD, node);
 468                 break;
 469             case COMPOUND_AND:
 470                 lowerCompoundBinaryOperator(leftType.equals(TypeList.BOOLEAN)
 471                         ? OperatorKind.AND : OperatorKind.BIT_AND, node);
 472                 break;
 473             case COMPOUND_OR:
 474                 lowerCompoundBinaryOperator(leftType.equals(TypeList.BOOLEAN)
 475                         ? OperatorKind.OR : OperatorKind.BIT_OR, node);
 476                 break;
 477             case COMPOUND_XOR:
 478                 lowerCompoundBinaryOperator(OperatorKind.BIT_XOR, node);
 479                 break;
 480             case COMPOUND_SHR:
 481                 lowerCompoundBinaryOperator(OperatorKind.SHR, node);
 482                 break;
 483             case COMPOUND_SHL:
 484                 lowerCompoundBinaryOperator(OperatorKind.SHL, node);
 485                 break;
 486             case COMPOUND_SAR:
 487                 lowerCompoundBinaryOperator(OperatorKind.SAR, node);
 488                 break;
 489             default:
 490                 throw new Error("Unsupported binary operator");
 491         }
 492         return EMPTY_BYTE_ARRAY;
 493     }
 494 
 495     private static int tmpObject;
 496     private void lowerCompoundBinaryOperator(OperatorKind kind, IRNode node) {
 497         IRNode left = node.getChild(Operator.Order.LEFT.ordinal());
 498         IRNode right = node.getChild(Operator.Order.RIGHT.ordinal());
 499 
 500         if (left instanceof NonStaticMemberVariable) {
 501             NonStaticMemberVariable var = (NonStaticMemberVariable) left;
 502             IRNode holder = var.getChild(0);
 503             Type type = holder.getResultType();
 504             VariableInfo tmpInfo = new VariableInfo("tmpObject_" + tmpObject++,
 505                     currentClass, type, VariableInfo.LOCAL);
 506             new Statement(new VariableInitialization(tmpInfo, holder), true).accept(this);
 507             left = new NonStaticMemberVariable(new LocalVariable(tmpInfo), var.getVariableInfo());
 508         }
 509         Type leftType = left.getResultType();
 510         Type rightType = right.getResultType();
 511         Type resultType = leftType;
 512         if (leftType instanceof BuiltInType && rightType instanceof BuiltInType) {
 513             if (kind != OperatorKind.SHL && kind != OperatorKind.SHR && kind != OperatorKind.SAR
 514                     && ((BuiltInType) rightType).isMoreCapaciousThan((BuiltInType) leftType)) {
 515                 resultType = rightType;
 516             }
 517         }
 518         IRNode result = new CastOperator(leftType,
 519                 new BinaryOperator(kind, resultType, left, right));
 520         new BinaryOperator(OperatorKind.ASSIGN, leftType, left, result).accept(this);
 521     }
 522 
 523     private void generateBasicLogicOperator(int ifOpcode, boolean retTrueFirst, IRNode left,
 524             IRNode right) {
 525         Label secondCase = new Label();
 526         Label endLabel = new Label();
 527         left.accept(this);
 528         currentMV.visitJumpInsn(ifOpcode, secondCase);
 529         right.accept(this);
 530         currentMV.visitJumpInsn(ifOpcode, secondCase);
 531         currentMV.visitInsn(retTrueFirst ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
 532         currentMV.visitJumpInsn(Opcodes.GOTO, endLabel);
 533         currentMV.visitLabel(secondCase);
 534         currentMV.visitInsn(retTrueFirst ? Opcodes.ICONST_0 : Opcodes.ICONST_1);
 535         currentMV.visitLabel(endLabel);
 536     }
 537 
 538     private void generateCmpBasedCode(Type type, int nonIntOpcode, int intOpcode) {
 539         boolean useNonIntOpcode = false;
 540         if (type.equals(TypeList.LONG) || type.equals(TypeList.FLOAT)
 541                 || type.equals(TypeList.DOUBLE)) {
 542             if (type.equals(TypeList.LONG)) {
 543                 currentMV.visitInsn(Opcodes.LCMP);
 544             } else if (type.equals(TypeList.FLOAT)) {
 545                 currentMV.visitInsn(Opcodes.FCMPL);
 546             } else {
 547                 currentMV.visitInsn(Opcodes.DCMPL);
 548             }
 549             useNonIntOpcode = true;
 550         }
 551         int opcodeToUse;
 552         if (!useNonIntOpcode) {
 553             if (type instanceof TypeKlass) {
 554                 if (intOpcode == Opcodes.IF_ICMPEQ) {
 555                     opcodeToUse = Opcodes.IF_ACMPEQ;
 556                 } else if (intOpcode == Opcodes.IF_ICMPNE) {
 557                     opcodeToUse = Opcodes.IF_ACMPNE;
 558                 } else {
 559                     throw new Error("Can't compare references");
 560                 }
 561             } else {
 562                 opcodeToUse = intOpcode;
 563             }
 564         } else {
 565             opcodeToUse = nonIntOpcode;
 566         }
 567         Label retTrue = new Label();
 568         Label end = new Label();
 569         currentMV.visitJumpInsn(opcodeToUse, retTrue);
 570         currentMV.visitInsn(Opcodes.ICONST_0);
 571         currentMV.visitJumpInsn(Opcodes.GOTO, end);
 572         currentMV.visitLabel(retTrue);
 573         currentMV.visitInsn(Opcodes.ICONST_1);
 574         currentMV.visitLabel(end);
 575     }
 576 
 577     /*
 578      * Converts top-stack element from one builtin type to another
 579      */
 580     private void convertTopType(Type from, Type to) {
 581         if (!(from instanceof BuiltInType) || !(to instanceof BuiltInType) || from.equals(to)) {
 582             return; // skip
 583         }
 584         boolean castedToInt = false;
 585         if (from.equals(TypeList.FLOAT)) {
 586             if (to.equals(TypeList.DOUBLE)) {
 587                 currentMV.visitInsn(Opcodes.F2D);
 588             } else if (to.equals(TypeList.LONG)) {
 589                 currentMV.visitInsn(Opcodes.F2L);
 590             } else {
 591                 currentMV.visitInsn(Opcodes.F2I);
 592                 castedToInt = true;
 593             }
 594         } else if (from.equals(TypeList.DOUBLE)) {
 595             if (to.equals(TypeList.FLOAT)) {
 596                 currentMV.visitInsn(Opcodes.D2F);
 597             } else if (to.equals(TypeList.LONG)) {
 598                 currentMV.visitInsn(Opcodes.D2L);
 599             } else {
 600                 currentMV.visitInsn(Opcodes.D2I);
 601                 castedToInt = true;
 602             }
 603         } else if (from.equals(TypeList.LONG)) {
 604             if (to.equals(TypeList.DOUBLE)) {
 605                 currentMV.visitInsn(Opcodes.L2D);
 606             } else if (to.equals(TypeList.FLOAT)) {
 607                 currentMV.visitInsn(Opcodes.L2F);
 608             } else {
 609                 currentMV.visitInsn(Opcodes.L2I);
 610                 castedToInt = true;
 611             }
 612         } else {
 613             if (to.equals(TypeList.BYTE)) {
 614                 currentMV.visitInsn(Opcodes.I2B);
 615             } else if (to.equals(TypeList.CHAR)) {
 616                 currentMV.visitInsn(Opcodes.I2C);
 617             } else if (to.equals(TypeList.SHORT)) {
 618                 currentMV.visitInsn(Opcodes.I2S);
 619             } else if (to.equals(TypeList.LONG)) {
 620                 currentMV.visitInsn(Opcodes.I2L);
 621             } else if (to.equals(TypeList.FLOAT)) {
 622                 currentMV.visitInsn(Opcodes.I2F);
 623             } else if (to.equals(TypeList.DOUBLE)) {
 624                 currentMV.visitInsn(Opcodes.I2D);
 625             }
 626         }
 627         if (castedToInt) {
 628             if (to.equals(TypeList.BYTE)) {
 629                 currentMV.visitInsn(Opcodes.I2B);
 630             } else if (to.equals(TypeList.CHAR)) {
 631                 currentMV.visitInsn(Opcodes.I2C);
 632             } else if (to.equals(TypeList.SHORT)) {
 633                 currentMV.visitInsn(Opcodes.I2S);
 634             }
 635         }
 636     }
 637 
 638     @Override
 639     public byte[] visit(Block node) {
 640         return iterateBlock(node);
 641     }
 642 
 643     @Override
 644     public byte[] visit(Break node) {
 645         Label label = endLabels.peek();
 646         if (label != null) {
 647             currentMV.visitJumpInsn(Opcodes.GOTO, label);
 648         }
 649         return EMPTY_BYTE_ARRAY;
 650     }
 651 
 652     @Override
 653     public byte[] visit(CastOperator node) {
 654         IRNode expression = node.getChild(0);
 655         expression.accept(this);
 656         Type to = node.getResultType();
 657         Type from = expression.getResultType();
 658         // TODO boxing/unboxing
 659         if (!TypeList.isBuiltIn(to) || !TypeList.isBuiltIn(from)) {
 660             // class cast
 661             currentMV.visitTypeInsn(Opcodes.CHECKCAST, asInternalName(to.getName()));
 662         } else {
 663             convertTopType(from, to);
 664         }
 665         return EMPTY_BYTE_ARRAY;
 666     }
 667 
 668     @Override
 669     public byte[] visit(CatchBlock node) {
 670         Type type = node.throwables.get(0);
 671         VariableInfo exInfo = new VariableInfo("ex", currentClass,
 672                 type, VariableInfo.LOCAL);
 673         int index = locals.getLocalIndex(exInfo);
 674         currentMV.visitVarInsn(Opcodes.ASTORE, index);
 675         node.getChild(0).accept(this);
 676         return EMPTY_BYTE_ARRAY;
 677     }
 678 
 679     @Override
 680     public byte[] visit(ClassDefinitionBlock node) {
 681         return iterateBlock(node);
 682     }
 683 
 684     @Override
 685     public byte[] visit(ConstructorDefinition node) {
 686         FunctionInfo info = node.getFunctionInfo();
 687         String ownerName = node.getOwner().getName();
 688         TypeKlass parentClass = currentClass.getParent();
 689         ContextDependedClassWriter cw = classWriters.get(ownerName);
 690 
 691         String descriptor = getDescriptor(node, 1, "V");
 692         currentMV = cw.visitMethod(asAccessFlags(info), "<init>", descriptor, null, null);
 693         currentMV.visitVarInsn(Opcodes.ALOAD, 0);
 694         currentMV.visitMethodInsn(Opcodes.INVOKESPECIAL,
 695                 parentClass != null ? asInternalName(parentClass.getName()) : "java/lang/Object",
 696                 "<init>", "()V", false);
 697         locals.initConstructorArguments(node.getOwner(), info);
 698         // TODO: add datamemebers as child to all ctors
 699         generateDataMembers(node.getParent().getParent().getChild(Klass.KlassPart.DATA_MEMBERS.ordinal()));
 700         IRNode body = node.getChild(0);
 701         body.accept(this);
 702         currentMV.visitInsn(Opcodes.RETURN);
 703         currentMV.visitMaxs(0, 0);
 704         currentMV.visitEnd();
 705         return EMPTY_BYTE_ARRAY;
 706     }
 707 
 708     private void generateDataMembers(IRNode node) {
 709         // TODO shouldn't we skip declaration?
 710         if (node != null) {
 711             node.accept(this);
 712         }
 713     }
 714 
 715     @Override
 716     public byte[] visit(ConstructorDefinitionBlock node) {
 717         return iterateBlock(node);
 718     }
 719 
 720     @Override
 721     public byte[] visit(Continue node) {
 722         Label label = beginLabels.peek();
 723         if (label != null) {
 724             currentMV.visitJumpInsn(Opcodes.GOTO, label);
 725         }
 726         return EMPTY_BYTE_ARRAY;
 727     }
 728 
 729     @Override
 730     public byte[] visit(CounterInitializer node) {
 731         visitLocalVar(node);
 732         emitPop(node.getVariableInfo().type);
 733         return EMPTY_BYTE_ARRAY;
 734     }
 735 
 736     private byte[] visitLocalVar(Initialization node) {
 737         VariableInfo vi = node.getVariableInfo();
 738         int index = locals.addLocal(vi);
 739         int store;
 740         node.getChild(0).accept(this); // place initialization expression on stack
 741         emitDup(vi.type);
 742         if (vi.type instanceof TypeKlass) {
 743             store = Opcodes.ASTORE;
 744         } else if (vi.type.equals(TypeList.DOUBLE)) {
 745             store = Opcodes.DSTORE;
 746         } else if (vi.type.equals(TypeList.LONG)) {
 747             store = Opcodes.LSTORE;
 748         } else if (vi.type.equals(TypeList.FLOAT)) {
 749             store = Opcodes.FSTORE;
 750         } else {
 751             store = Opcodes.ISTORE;
 752         }
 753         currentMV.visitVarInsn(store, index);
 754         return EMPTY_BYTE_ARRAY;
 755     }
 756 
 757     @Override
 758     public byte[] visit(CounterManipulator node) {
 759         return node.getChild(0).accept(this);
 760     }
 761 
 762     @Override
 763     public byte[] visit(Declaration node) {
 764         IRNode child = node.getChild(0);
 765         child.accept(this);
 766         if (child instanceof Initialization) {
 767             emitPop(((Initialization) child).getVariableInfo().type);
 768         }
 769         return EMPTY_BYTE_ARRAY;
 770     }
 771 
 772     @Override
 773     public byte[] visit(DoWhile node) {
 774         Loop loop = node.getLoop();
 775         loop.initialization.accept(this);
 776         node.getChild(DoWhile.DoWhilePart.HEADER.ordinal()).accept(this);
 777         Label currentLoopBegin = new Label();
 778         beginLabels.push(currentLoopBegin);
 779         Label currentLoopEnd = new Label();
 780         endLabels.push(currentLoopEnd);
 781         currentMV.visitLabel(currentLoopBegin);
 782         node.getChild(DoWhile.DoWhilePart.BODY1.ordinal()).accept(this);
 783         loop.manipulator.accept(this);
 784         node.getChild(DoWhile.DoWhilePart.BODY2.ordinal()).accept(this);
 785         loop.condition.accept(this);
 786         assert loop.condition.getResultType() == TypeList.BOOLEAN;
 787         currentMV.visitJumpInsn(Opcodes.IFEQ, currentLoopBegin);
 788         currentMV.visitLabel(currentLoopEnd);
 789         Label a = beginLabels.pop();
 790         assert currentLoopBegin == a;
 791         a = endLabels.pop();
 792         assert currentLoopEnd == a;
 793         return EMPTY_BYTE_ARRAY;
 794     }
 795 
 796     @Override
 797     public byte[] visit(For node) {
 798         Loop loop = node.getLoop();
 799         loop.initialization.accept(this);
 800         node.getChild(For.ForPart.HEADER.ordinal()).accept(this);
 801         node.getChild(For.ForPart.STATEMENT1.ordinal()).accept(this);
 802         Label currentLoopBegin = new Label();
 803         beginLabels.push(currentLoopBegin);
 804         currentMV.visitLabel(currentLoopBegin);
 805         loop.condition.accept(this);
 806         assert loop.condition.getResultType() == TypeList.BOOLEAN;
 807         Label currentLoopEnd = new Label();
 808         endLabels.push(currentLoopEnd);
 809         currentMV.visitJumpInsn(Opcodes.IFEQ, currentLoopEnd);
 810         node.getChild(For.ForPart.STATEMENT2.ordinal()).accept(this);
 811         node.getChild(For.ForPart.BODY1.ordinal()).accept(this);
 812         loop.manipulator.accept(this);
 813         node.getChild(For.ForPart.BODY2.ordinal()).accept(this);
 814         node.getChild(For.ForPart.BODY3.ordinal()).accept(this);
 815         currentMV.visitJumpInsn(Opcodes.GOTO, currentLoopBegin);
 816         currentMV.visitLabel(currentLoopEnd);
 817         Label a = beginLabels.pop();
 818         assert currentLoopBegin == a;
 819         a = endLabels.pop();
 820         assert currentLoopEnd == a;
 821         return EMPTY_BYTE_ARRAY;
 822     }
 823 
 824     @Override
 825     public byte[] visit(Function node) {
 826         FunctionInfo info = node.getValue();
 827         boolean needInstance = !info.isStatic() && !info.isConstructor();
 828         if (needInstance) {
 829             node.getChild(0).accept(this); // placing instance on stack
 830         }
 831         // call itself with specific invoke*
 832         String signature = info.argTypes.stream()
 833                 .skip(!needInstance ? 0 : 1)
 834                 .map(vi -> new String(vi.type.accept(this)))
 835                 .collect(Collectors.joining("", "(", ")"))
 836                 + (info.isConstructor() ? "V" : new String(node.getResultType().accept(this)));
 837         int invokeCode = Opcodes.INVOKEVIRTUAL;
 838         if (info.isStatic()) {
 839             invokeCode = Opcodes.INVOKESTATIC;
 840         } else if (info.isConstructor() || info.isPrivate()) {
 841             // TODO : superclass method invocation?
 842             invokeCode = Opcodes.INVOKESPECIAL;
 843         } else {
 844             if (info.owner.isInterface()) {
 845                 invokeCode = Opcodes.INVOKEINTERFACE;
 846             }
 847         }
 848         if (info.isConstructor()) {
 849             currentMV.visitTypeInsn(Opcodes.NEW, asInternalName(info.owner.getName()));
 850             currentMV.visitInsn(Opcodes.DUP);
 851         }
 852         // calculating parameters
 853         node.getChildren().stream()
 854                 .skip(!needInstance ? 0 : 1)
 855                 .forEachOrdered(c -> c.accept(this));
 856         currentMV.visitMethodInsn(invokeCode, asInternalName(info.owner.getName()),
 857                 info.isConstructor() ? "<init>" : info.name, signature,
 858                 invokeCode == Opcodes.INVOKEINTERFACE);
 859         return EMPTY_BYTE_ARRAY;
 860     }
 861 
 862     @Override
 863     public byte[] visit(FunctionDeclaration node) {
 864         FunctionInfo info = node.getFunctionInfo();
 865         String ownerName = node.getOwner().getName();
 866         ContextDependedClassWriter cw = classWriters.get(ownerName);
 867         String returnType = new String(info.type.accept(this));
 868 
 869         String descriptor = getDescriptor(node, 0, returnType);
 870         currentMV = cw.visitMethod(asAccessFlags(info) + Opcodes.ACC_ABSTRACT,
 871                 info.name, descriptor, null, null);
 872         currentMV.visitEnd();
 873         return EMPTY_BYTE_ARRAY;
 874     }
 875 
 876     @Override
 877     public byte[] visit(FunctionDeclarationBlock node) {
 878         return iterateBlock(node);
 879     }
 880 
 881     @Override
 882     public byte[] visit(FunctionDefinition node) {
 883         FunctionInfo info = node.getFunctionInfo();
 884         String ownerName = node.getOwner().getName();
 885         ContextDependedClassWriter cw = classWriters.get(ownerName);
 886         String returnType = new String(info.type.accept(this));
 887 
 888         String descriptor = getDescriptor(node, 2, returnType);
 889         currentMV = cw.visitMethod(asAccessFlags(info), info.name, descriptor, null, null);
 890         locals.initFunctionArguments(info);
 891         IRNode body = node.getChild(0);
 892         body.accept(this);
 893         IRNode ret = node.getChild(1);
 894         ret.accept(this);
 895         currentMV.visitMaxs(0, 0);
 896         currentMV.visitEnd();
 897         return EMPTY_BYTE_ARRAY;
 898     }
 899 
 900     @Override
 901     public byte[] visit(FunctionDefinitionBlock node) {
 902         return iterateBlock(node);
 903     }
 904 
 905     @Override
 906     public byte[] visit(FunctionRedefinition node) {
 907         FunctionInfo info = node.getFunctionInfo();
 908         String ownerName = node.getOwner().getName();
 909         ContextDependedClassWriter cw = classWriters.get(ownerName);
 910         String returnType = new String(info.type.accept(this));
 911         String descriptor = getDescriptor(node, 2, returnType);
 912         currentMV = cw.visitMethod(asAccessFlags(info), info.name, descriptor, null, null);
 913         locals.initFunctionArguments(info);
 914         IRNode body = node.getChild(0);
 915         body.accept(this);
 916         IRNode ret = node.getChild(1);
 917         ret.accept(this);
 918         currentMV.visitMaxs(0, 0);
 919         currentMV.visitEnd();
 920         return EMPTY_BYTE_ARRAY;
 921     }
 922 
 923     @Override
 924     public byte[] visit(FunctionRedefinitionBlock node) {
 925         return iterateBlock(node);
 926     }
 927 
 928     @Override
 929     public byte[] visit(If node) {
 930         IRNode conditionBlock = node.getChild(If.IfPart.CONDITION.ordinal());
 931         // get the condition type to emit correct if
 932         conditionBlock.accept(this);
 933         generateIf(Opcodes.IFEQ, node.getChild(If.IfPart.THEN.ordinal()),
 934                 node.getChild(If.IfPart.ELSE.ordinal()));
 935         return EMPTY_BYTE_ARRAY;
 936     }
 937 
 938     /*
 939      * Generates if block with then and else blocks for the given IF opcode
 940      */
 941     private void generateIf(int ifOpcode, IRNode thenBlock, IRNode elseBlock) {
 942         Label elseLabel = new Label();
 943         // if the opposite condition is met then go to the else statement
 944         currentMV.visitJumpInsn(ifOpcode, elseLabel);
 945         // write THEN block
 946         thenBlock.accept(this);
 947         if (elseBlock != null) {
 948             // goto the end after THEN
 949             Label endLabel = new Label();
 950             currentMV.visitJumpInsn(Opcodes.GOTO, endLabel);
 951             // ELSE block
 952             currentMV.visitLabel(elseLabel);
 953             elseBlock.accept(this);
 954             currentMV.visitLabel(endLabel);
 955         } else {
 956             currentMV.visitLabel(elseLabel);
 957         }
 958     }
 959 
 960     @Override
 961     public byte[] visit(Initialization node) {
 962         VariableInfo vi = node.getVariableInfo();
 963         if (vi.isLocal()) {
 964             return visitLocalVar(node);
 965         }
 966         String ownerName = vi.getOwner().getName();
 967         ContextDependedClassWriter cw = classWriters.get(ownerName);
 968         String typeName = new String(vi.type.accept(this));
 969         // constant value used only for final static fields
 970         FieldVisitor fw = cw.visitField(asAccessFlags(vi), vi.name,
 971                 typeName,
 972                 null /* Generic */,
 973                 null /* Constant value */);
 974         fw.visitEnd(); // doesn't need visitAnnotation and visitAttribute
 975         if (vi.isStatic()) {
 976             node.getChild(0).accept(this); // put value to stack
 977             emitDup(vi.type);
 978             currentMV.visitFieldInsn(Opcodes.PUTSTATIC,
 979                     asInternalName(vi.getOwner().getName()),
 980                     vi.name,
 981                     new String(vi.type.accept(this)));
 982         } else {
 983             // TODO : can it be another object?
 984             currentMV.visitVarInsn(Opcodes.ALOAD, 0); // put this to stack
 985             node.getChild(0).accept(this); // put value to stack
 986             emitDupX1(vi.type);
 987             currentMV.visitFieldInsn(Opcodes.PUTFIELD,
 988                     asInternalName(vi.getOwner().getName()),
 989                     vi.name,
 990                     new String(vi.type.accept(this)));
 991         }
 992         return EMPTY_BYTE_ARRAY;
 993     }
 994 
 995     private void emitDupX1(Type type) {
 996         if (TypeList.DOUBLE.equals(type) || TypeList.LONG.equals(type)) {
 997             currentMV.visitInsn(Opcodes.DUP2_X1);
 998         } else if (!TypeList.VOID.equals(type)){
 999             currentMV.visitInsn(Opcodes.DUP_X1);
1000         }
1001     }
1002 
1003     private void emitDup(Type type) {
1004         if (TypeList.DOUBLE.equals(type) || TypeList.LONG.equals(type)) {
1005             currentMV.visitInsn(Opcodes.DUP2);
1006         } else if (!TypeList.VOID.equals(type)){
1007             currentMV.visitInsn(Opcodes.DUP);
1008         }
1009     }
1010 
1011     @Override
1012     public byte[] visit(Interface node) {
1013         String name = node.getName();
1014         ContextDependedClassWriter classWriter = new ContextDependedClassWriter(context, CLASS_WRITER_FLAGS);
1015         classWriters.put(name, classWriter);
1016         TypeKlass parentKlass = node.getParentKlass();
1017         classWriter.visit(Opcodes.V1_8,
1018                           Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE,
1019                           asInternalName(name),
1020                           null /* Generic */,
1021                           "java/lang/Object",
1022                           parentKlass == null ? null : new String[] {
1023                                   asInternalName(parentKlass.getName())});
1024         if (node.getChildren().size() > 0) {
1025             node.getChild(0).accept(this);
1026         }
1027 
1028         classWriter.visitEnd();
1029         byte[] byteCode = classWriter.toByteArray();
1030         context.register(name, byteCode);
1031         return byteCode;
1032     }
1033 
1034     @Override
1035     public byte[] visit(Klass node) {
1036         String name = node.getName();
1037         TypeKlass prevClass = currentClass;
1038         currentClass = node.getThisKlass();
1039         ContextDependedClassWriter classWriter = new ContextDependedClassWriter(context, CLASS_WRITER_FLAGS);
1040         classWriters.put(name, classWriter);
1041         TypeKlass thisClass = node.getThisKlass();
1042         TypeKlass parentClass = node.getParentKlass();
1043         String[] interfaces = node.getInterfaces().stream()
1044                 .map(IRNode::getName)
1045                 .map(ByteCodeVisitor::asInternalName)
1046                 .toArray(String[]::new);
1047         classWriter.visit(Opcodes.V1_8, asAccessFlags(thisClass),
1048                 asInternalName(name),
1049                 null /* Generic */,
1050                 parentClass != null ? asInternalName(parentClass.getName()) : "java/lang/Object",
1051                 interfaces);
1052 
1053         IRNode constructors = node.getChild(Klass.KlassPart.CONSTRUCTORS.ordinal());
1054         if (constructors != null) {
1055             constructors.accept(this);
1056         } else {
1057             // generate default ctor
1058             currentMV = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
1059             currentMV.visitVarInsn(Opcodes.ALOAD, 0);
1060             currentMV.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
1061             locals.clear();
1062             locals.addLocal(new VariableInfo("this", thisClass, thisClass, VariableInfo.NONE));
1063             generateDataMembers(node.getChild(Klass.KlassPart.DATA_MEMBERS.ordinal()));
1064             currentMV.visitInsn(Opcodes.RETURN);
1065             currentMV.visitMaxs(0, 0);
1066             currentMV.visitEnd();
1067         }
1068         IRNode redefinedFunctions = node.getChild(Klass.KlassPart.REDEFINED_FUNCTIONS.ordinal());
1069         if (redefinedFunctions != null) {
1070             redefinedFunctions.accept(this);
1071         }
1072         IRNode overridenFunctions = node.getChild(Klass.KlassPart.OVERRIDEN_FUNCTIONS.ordinal());
1073         if (overridenFunctions != null) {
1074             overridenFunctions.accept(this);
1075         }
1076         IRNode memberFunctions = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS.ordinal());
1077         if (memberFunctions != null) {
1078             memberFunctions.accept(this);
1079         }
1080         IRNode memberFunctionDecls = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS_DECLARATIONS.ordinal());
1081         if (memberFunctionDecls != null) {
1082             memberFunctionDecls.accept(this);
1083         }
1084         IRNode printVariables = node.getChild(Klass.KlassPart.PRINT_VARIABLES.ordinal());
1085         if (printVariables != null) {
1086             printVariables.accept(this);
1087         }
1088         classWriter.visitEnd();
1089         byte[] byteCode = classWriter.toByteArray();
1090         context.register(name, byteCode);
1091         currentClass = prevClass;
1092         return byteCode;
1093     }
1094 
1095     private void visitLiteral(boolean value) {
1096         double chance = PseudoRandom.random();
1097         if (chance < CONSTANT_INST_CHANCE) {
1098             currentMV.visitInsn(value ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
1099         } else {
1100             currentMV.visitIntInsn(Opcodes.BIPUSH, value ? 1 : 0);
1101         }
1102     }
1103 
1104     private void visitLiteral(byte value) {
1105         double chance = PseudoRandom.random();
1106         if (chance < CONSTANT_INST_CHANCE && value > -2 && value < 6) {
1107             currentMV.visitInsn(Opcodes.ICONST_0 + value);
1108         } else {
1109             currentMV.visitIntInsn(Opcodes.BIPUSH, value);
1110         }
1111     }
1112 
1113     private void visitLiteral(short value) {
1114         double chance = PseudoRandom.random();
1115         if (chance < CONSTANT_INST_CHANCE && value > -2 && value < 6) {
1116             currentMV.visitInsn(Opcodes.ICONST_0 + value);
1117         } else {
1118             currentMV.visitIntInsn(Opcodes.SIPUSH, value);
1119         }
1120     }
1121 
1122     private void visitLiteral(char value) {
1123         double chance = PseudoRandom.random();
1124         if (chance < CONSTANT_INST_CHANCE && value < 6) {
1125             currentMV.visitInsn(Opcodes.ICONST_0 + value);
1126         } else {
1127             // TODO : check for widechar/unicode
1128             currentMV.visitIntInsn(Opcodes.BIPUSH, value);
1129         }
1130     }
1131 
1132     private void visitLiteral(int value) {
1133         double chance = PseudoRandom.random();
1134         if (chance < CONSTANT_INST_CHANCE && value > -2 && value < 6) {
1135             currentMV.visitInsn(Opcodes.ICONST_0 + value);
1136         } else {
1137             currentMV.visitLdcInsn(value);
1138         }
1139     }
1140 
1141     private void visitLiteral(long value) {
1142         double chance = PseudoRandom.random();
1143         if (chance < CONSTANT_INST_CHANCE && value > -1 && value < 2) {
1144             currentMV.visitInsn(Opcodes.LCONST_0 + (int)value);
1145         } else {
1146             currentMV.visitLdcInsn(value);
1147         }
1148     }
1149 
1150     private void visitLiteral(float value) {
1151         double chance = PseudoRandom.random();
1152         if (chance < CONSTANT_INST_CHANCE && (value == 0.0f || value == 1.0f || value == 2.0f)) {
1153             currentMV.visitInsn(Opcodes.FCONST_0 + (int)value);
1154         } else {
1155             currentMV.visitLdcInsn(value);
1156         }
1157     }
1158 
1159     private void visitLiteral(double value) {
1160         double chance = PseudoRandom.random();
1161         if (chance < CONSTANT_INST_CHANCE && (value == 0.0 || value == 1.0)) {
1162             currentMV.visitInsn(Opcodes.DCONST_0 + (int)value);
1163         } else {
1164             currentMV.visitLdcInsn(value);
1165         }
1166     }
1167 
1168     @Override
1169     public byte[] visit(Literal node) {
1170         /*
1171             ICONST_n (−1 ≤ n ≤ 5) <==> BIPUSH <n>
1172             LCONST_n (0 ≤ n ≤ 1)
1173             FCONST_n (0 ≤ n ≤ 2)
1174             DCONST_n (0 ≤ n ≤ 1)
1175             ACONST_NULL
1176 
1177             BIPUSH b, −128 ≤ b < 127
1178             SIPUSH s, −32768 ≤ s < 32767
1179             LDC cst (int, float, long, double, String or Type)
1180         */
1181         Type type = node.getResultType();
1182         double chance = PseudoRandom.random();
1183         if (type.equals(TypeList.BOOLEAN)) {
1184             visitLiteral(Boolean.valueOf(node.getValue().toString()));
1185         } else if (type.equals(TypeList.BYTE)) {
1186             visitLiteral(Byte.valueOf(node.getValue().toString()));
1187         } else if (type.equals(TypeList.SHORT)) {
1188             visitLiteral(Short.valueOf(node.getValue().toString()));
1189         } else if (type.equals(TypeList.CHAR)) {
1190             visitLiteral(node.getValue().toString().charAt(0));
1191         } else if (type.equals(TypeList.INT)) {
1192             visitLiteral(Integer.valueOf(node.getValue().toString()));
1193         } else if (type.equals(TypeList.LONG)) {
1194             visitLiteral(Long.valueOf(node.getValue().toString()));
1195         } else if (type.equals(TypeList.FLOAT)) {
1196             visitLiteral(Float.valueOf(node.getValue().toString()));
1197         } else if (type.equals(TypeList.DOUBLE)) {
1198             visitLiteral(Double.valueOf(node.getValue().toString()));
1199         } else {
1200             currentMV.visitLdcInsn(node.getValue());
1201         }
1202         return EMPTY_BYTE_ARRAY;
1203     }
1204     private static final double CONSTANT_INST_CHANCE = 0.5;
1205 
1206     @Override
1207     public byte[] visit(LocalVariable node) {
1208         // This node is for "reading" only. Writing is handled in BinaryOperator visit(see ASSIGN)
1209         VariableInfo vi = node.getVariableInfo();
1210         Type varType = vi.type;
1211         int index = locals.getLocalIndex(vi);
1212         if (varType.equals(TypeList.LONG)) {
1213             currentMV.visitVarInsn(Opcodes.LLOAD, index);
1214         } else if (varType.equals(TypeList.DOUBLE)) {
1215             currentMV.visitVarInsn(Opcodes.DLOAD, index);
1216         } else if (varType.equals(TypeList.FLOAT)) {
1217             currentMV.visitVarInsn(Opcodes.FLOAD, index);
1218         } else if (varType instanceof TypeKlass) {
1219             currentMV.visitVarInsn(Opcodes.ALOAD, index);
1220         } else {
1221             currentMV.visitVarInsn(Opcodes.ILOAD, index);
1222         }
1223         return EMPTY_BYTE_ARRAY;
1224     }
1225 
1226     @Override
1227     public byte[] visit(LoopingCondition node) {
1228         return node.getCondition().accept(this);
1229     }
1230 
1231     @Override
1232     public byte[] visit(MainKlass node) {
1233         TypeKlass prevClass = currentClass;
1234         currentClass = node.getThisKlass();
1235         String name = node.getName();
1236         ContextDependedClassWriter mainClassWriter = new ContextDependedClassWriter(context, CLASS_WRITER_FLAGS);
1237         classWriters.put(name, mainClassWriter);
1238 
1239         TypeKlass thisClass = node.getThisKlass();
1240         mainClassWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,
1241                 asInternalName(name),
1242                 null /* Generic */,
1243                 "java/lang/Object",
1244                 null /* interfaces */);
1245         // TODO: constructor for main class
1246         currentMV = mainClassWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
1247         currentMV.visitVarInsn(Opcodes.ALOAD, 0);
1248         currentMV.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
1249         locals.clear();
1250         locals.addLocal(new VariableInfo("this", thisClass, thisClass, VariableInfo.NONE));
1251         generateDataMembers(node.getChild(MainKlass.MainKlassPart.DATA_MEMBERS.ordinal()));
1252         currentMV.visitInsn(Opcodes.RETURN);
1253         currentMV.visitMaxs(0, 0);
1254         currentMV.visitEnd();
1255 
1256         IRNode memberFunctions = node.getChild(MainKlass.MainKlassPart.MEMBER_FUNCTIONS.ordinal());
1257         if (memberFunctions != null) {
1258             memberFunctions.accept(this);
1259         }
1260         IRNode testFunction = node.getChild(MainKlass.MainKlassPart.TEST_FUNCTION.ordinal());
1261         if (testFunction != null) {
1262             currentMV = mainClassWriter.visitMethod(
1263                     Opcodes.ACC_PRIVATE,
1264                     "test",
1265                     "()V",
1266                     null,
1267                     null);
1268             locals.clear();
1269             locals.addLocal(new VariableInfo("this", thisClass, thisClass, VariableInfo.NONE));
1270             testFunction.accept(this);
1271             currentMV.visitInsn(Opcodes.RETURN);
1272             currentMV.visitMaxs(0, 0);
1273             currentMV.visitEnd();
1274         }
1275         IRNode printVariables = node.getChild(MainKlass.MainKlassPart.PRINT_VARIABLES.ordinal());
1276         if (printVariables != null) {
1277             printVariables.accept(this);
1278         }
1279 
1280         mainClassWriter.visitEnd();
1281 
1282         byte[] byteCode = mainClassWriter.toByteArray();
1283         context.register(name, byteCode);
1284         currentClass = prevClass;
1285         return byteCode;
1286     }
1287 
1288     @Override
1289     public byte[] visit(NonStaticMemberVariable node) {
1290         // This node is for "reading" only. Writing is handled in BinaryOperator visit(see ASSIGN)
1291         VariableInfo vi = node.getVariableInfo();
1292         // put object to stack
1293         node.getChild(0).accept(this);
1294         currentMV.visitFieldInsn(Opcodes.GETFIELD, asInternalName(vi.getOwner().getName()), vi.name,
1295                 new String(vi.type.accept(this)));
1296         return EMPTY_BYTE_ARRAY;
1297     }
1298 
1299     @Override
1300     public byte[] visit(Nothing node) {
1301         // TODO : add randomness
1302         currentMV.visitInsn(Opcodes.NOP);
1303         return EMPTY_BYTE_ARRAY;
1304     }
1305 
1306     @Override
1307     public byte[] visit(PrintVariables node) {
1308         return FixedTrees.printVariablesAsFunction(node).accept(this);
1309     }
1310 
1311     @Override
1312     public byte[] visit(Return node) {
1313         node.getExpression().accept(this);
1314         Type result = node.getResultType();
1315         if (result instanceof TypeKlass) {
1316             currentMV.visitInsn(Opcodes.ARETURN);
1317         } else if (result.equals(TypeList.VOID)) {
1318             currentMV.visitInsn(Opcodes.RETURN);
1319         } else if (result.equals(TypeList.DOUBLE)) {
1320             currentMV.visitInsn(Opcodes.DRETURN);
1321         } else if (result.equals(TypeList.FLOAT)) {
1322             currentMV.visitInsn(Opcodes.FRETURN);
1323         } else if (result.equals(TypeList.LONG)) {
1324             currentMV.visitInsn(Opcodes.LRETURN);
1325         } else {
1326             currentMV.visitInsn(Opcodes.IRETURN);
1327         }
1328         return EMPTY_BYTE_ARRAY;
1329     }
1330 
1331     @Override
1332     public byte[] visit(Statement node) {
1333         IRNode child = node.getChild(0);
1334         child.accept(this);
1335         Type resultType = child.getResultType();
1336         emitPop(resultType);
1337         return EMPTY_BYTE_ARRAY;
1338     }
1339 
1340     private void emitPop(Type resultType) {
1341         if (resultType.equals(TypeList.LONG) || resultType.equals(TypeList.DOUBLE)) {
1342             currentMV.visitInsn(Opcodes.POP2);
1343         } else if (!resultType.equals(TypeList.VOID)) {
1344             currentMV.visitInsn(Opcodes.POP);
1345         }
1346     }
1347 
1348     @Override
1349     public byte[] visit(StaticConstructorDefinition node) {
1350         String ownerName = node.getOwner().getName();
1351         ContextDependedClassWriter cw = classWriters.get(ownerName);
1352         String descriptor = getDescriptor(node, 1, "V");
1353         currentMV = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", descriptor, null, null);
1354         locals.clear();
1355         IRNode body = node.getChild(0);
1356         body.accept(this);
1357         currentMV.visitInsn(Opcodes.RETURN);
1358         currentMV.visitMaxs(0, 0);
1359         currentMV.visitEnd();
1360         return EMPTY_BYTE_ARRAY;
1361     }
1362 
1363     @Override
1364     public byte[] visit(StaticMemberVariable node) {
1365         // This node is for "reading" only. Writing is handled in BinaryOperator visit(see ASSIGN)
1366         VariableInfo vi = node.getVariableInfo();
1367         currentMV.visitFieldInsn(Opcodes.GETSTATIC,
1368                 asInternalName(vi.getOwner().getName()),
1369                 vi.name,
1370                 new String(vi.type.accept(this)));
1371         return EMPTY_BYTE_ARRAY;
1372     }
1373 
1374     @Override
1375     public byte[] visit(Switch node) {
1376         node.getChild(0).accept(this);
1377         int caseBlockIdx = node.getCaseBlockIndex();
1378         Label defaultCase = new Label();
1379         IRNode defaultBlock = null;
1380         SortedMap<Integer, Pair<Label, IRNode>> cases = new TreeMap<>();
1381         for (int i = 0; i < caseBlockIdx - 1; ++i) {
1382             if (node.getChild(i + 1) instanceof Nothing) {
1383                 defaultBlock = node.getChild(i + caseBlockIdx);
1384             } else {
1385                 Literal literal = (Literal) node.getChild(i + 1);
1386                 int value = 0;
1387                 if (literal.value instanceof Integer) {
1388                     value = (Integer) literal.value;
1389                 } else if (literal.value instanceof Short) {
1390                     value = (Short) literal.value;
1391                 } else if (literal.value instanceof Byte) {
1392                     value = (Byte) literal.value;
1393                 } else if (literal.value instanceof Character) {
1394                     value = (Character) literal.value;
1395                 }
1396                 cases.put(value, new Pair<>(new Label(), node.getChild(i + caseBlockIdx)));
1397             }
1398         }
1399         Label breakLabel = new Label();
1400         endLabels.push(breakLabel);
1401         currentMV.visitLookupSwitchInsn(defaultCase,
1402                 cases.keySet().stream()
1403                         .mapToInt(Integer::intValue)
1404                         .toArray(),
1405                 cases.values().stream()
1406                         .map(p -> p.first)
1407                         .toArray(Label[]::new));
1408         for (Pair<Label, IRNode> p : cases.values()) {
1409             currentMV.visitLabel(p.first);
1410             p.second.accept(this);
1411         }
1412         currentMV.visitLabel(defaultCase);
1413         if (defaultBlock != null) {
1414             defaultBlock.accept(this);
1415         }
1416         Label a = endLabels.pop();
1417         assert breakLabel == a;
1418         currentMV.visitLabel(breakLabel);
1419         return EMPTY_BYTE_ARRAY;
1420     }
1421 
1422     @Override
1423     public byte[] visit(TernaryOperator node) {
1424         IRNode conditionBlock = node.getChild(TernaryOperator.TernaryPart.CONDITION.ordinal());
1425         // get the condition type to emit correct if
1426         conditionBlock.accept(this);
1427         generateIf(Opcodes.IFEQ, node.getChild(TernaryOperator.TernaryPart.TRUE.ordinal()),
1428                 node.getChild(TernaryOperator.TernaryPart.FALSE.ordinal()));
1429         return EMPTY_BYTE_ARRAY;
1430     }
1431 
1432     @Override
1433     public byte[] visit(Throw node) {
1434         node.getThowable().accept(this);
1435         currentMV.visitInsn(Opcodes.ATHROW);
1436         return EMPTY_BYTE_ARRAY;
1437     }
1438 
1439     @Override
1440     public byte[] visit(TryCatchBlock node) {
1441         List<? extends IRNode> children = node.getChildren();
1442         IRNode tryBlock = children.get(0);
1443         IRNode finallyBlock = children.get(1);
1444         Label tryStart = new Label();
1445         Label tryEnd = new Label();
1446         Label finallyStart = new Label();
1447         Label finallyEnd = new Label();
1448 
1449         currentMV.visitLabel(tryStart);
1450         tryBlock.accept(this);
1451         currentMV.visitLabel(tryEnd);
1452         finallyBlock.accept(this);
1453         currentMV.visitJumpInsn(Opcodes.GOTO, finallyEnd);
1454         VariableInfo exInfo = new VariableInfo("ex", currentClass,
1455                 new TypeKlass("java.lang.Throwable"), VariableInfo.LOCAL);
1456         int index = locals.addLocal(exInfo);
1457         for (int i = 2; i < children.size(); ++i) {
1458             Label handlerBegin = new Label();
1459             Label handlerEnd = new Label();
1460             CatchBlock catchBlock = (CatchBlock) children.get(i);
1461             for (Type t : catchBlock.throwables) {
1462                 currentMV.visitTryCatchBlock(tryStart, tryEnd, handlerBegin, asInternalName(t.getName()));
1463             }
1464             currentMV.visitLabel(handlerBegin);
1465             catchBlock.accept(this);
1466             currentMV.visitLabel(handlerEnd);
1467             finallyBlock.accept(this);
1468             currentMV.visitJumpInsn(Opcodes.GOTO, finallyEnd);
1469             currentMV.visitTryCatchBlock(handlerBegin, handlerEnd, finallyStart, null);
1470         }
1471 
1472         currentMV.visitTryCatchBlock(tryStart, tryEnd, finallyStart, null);
1473         currentMV.visitLabel(finallyStart);
1474         currentMV.visitVarInsn(Opcodes.ASTORE, index);
1475         finallyBlock.accept(this);
1476         currentMV.visitVarInsn(Opcodes.ALOAD, index);
1477         currentMV.visitInsn(Opcodes.ATHROW);
1478         currentMV.visitLabel(finallyEnd);
1479         return EMPTY_BYTE_ARRAY;
1480     }
1481 
1482     @Override
1483     public byte[] visit(Type node) {
1484         String name;
1485         if (TypeList.isBuiltIn(node)) {
1486             switch (node.getName()) {
1487                 case "void":
1488                     name = "V";
1489                     break;
1490                 case "boolean":
1491                     name = "Z";
1492                     break;
1493                 case "byte":
1494                     name = "B";
1495                     break;
1496                 case "char":
1497                     name = "C";
1498                     break;
1499                 case "short":
1500                     name = "S";
1501                     break;
1502                 case "int":
1503                     name = "I";
1504                     break;
1505                 case "long":
1506                     name = "J";
1507                     break;
1508                 case "float":
1509                     name = "F";
1510                     break;
1511                 case "double":
1512                     name = "D";
1513                     break;
1514                 default:
1515                     throw new IllegalArgumentException("Unknown type '" + node.getName());
1516             }
1517         } else {
1518             name = "L" + asInternalName(node.getName()) + ";";
1519         }
1520         return name.getBytes();
1521     }
1522 
1523     @Override
1524     public byte[] visit(TypeArray node) {
1525         String name;
1526         String prefix = Stream.generate(() -> "[")
1527                 .limit(node.dimensions)
1528                 .collect(Collectors.joining());
1529         name = prefix + new String(node.getType().accept(this));
1530         return name.getBytes();
1531     }
1532 
1533     @Override
1534     public byte[] visit(UnaryOperator node) {
1535         OperatorKind opKind = node.getOperationKind();
1536         IRNode exp = node.getChild(0);
1537         // argument expression is handled separately for inc and dec operators
1538         if (opKind != OperatorKind.POST_DEC && opKind != OperatorKind.POST_INC
1539                 && opKind != OperatorKind.PRE_DEC && opKind != OperatorKind.PRE_INC) {
1540             exp.accept(this);
1541         }
1542         Type resultType = exp.getResultType();
1543         switch (opKind) {
1544             case NOT:
1545                 Label retTrueForNot = new Label();
1546                 Label endForNot = new Label();
1547                 currentMV.visitJumpInsn(Opcodes.IFEQ, retTrueForNot);
1548                 currentMV.visitInsn(Opcodes.ICONST_0);
1549                 currentMV.visitJumpInsn(Opcodes.GOTO, endForNot);
1550                 currentMV.visitLabel(retTrueForNot);
1551                 currentMV.visitInsn(Opcodes.ICONST_1);
1552                 currentMV.visitLabel(endForNot);
1553                 break;
1554             case BIT_NOT:
1555                 if (resultType.equals(TypeList.LONG)) {
1556                     currentMV.visitLdcInsn(-1L);
1557                     currentMV.visitInsn(Opcodes.LXOR);
1558                 } else {
1559                     currentMV.visitInsn(Opcodes.ICONST_M1);
1560                     currentMV.visitInsn(Opcodes.IXOR);
1561                 }
1562                 break;
1563             case UNARY_MINUS:
1564                 if (resultType.equals(TypeList.LONG)) {
1565                     currentMV.visitInsn(Opcodes.LNEG);
1566                 } else if (resultType.equals(TypeList.FLOAT)) {
1567                     currentMV.visitInsn(Opcodes.FNEG);
1568                 } else if (resultType.equals(TypeList.DOUBLE)) {
1569                     currentMV.visitInsn(Opcodes.DNEG);
1570                 } else {
1571                     currentMV.visitInsn(Opcodes.INEG);
1572                 }
1573                 break;
1574             case UNARY_PLUS:
1575                 break;
1576             case PRE_DEC:
1577                 lowerIncDecUnaryOperator(OperatorKind.SUB, true, node);
1578                 break;
1579             case POST_DEC:
1580                 lowerIncDecUnaryOperator(OperatorKind.SUB, false, node);
1581                 break;
1582             case PRE_INC:
1583                 lowerIncDecUnaryOperator(OperatorKind.ADD, true, node);
1584                 break;
1585             case POST_INC:
1586                 lowerIncDecUnaryOperator(OperatorKind.ADD, false, node);
1587                 break;
1588             default:
1589                 throw new RuntimeException("Incorrect unary operator: " + opKind);
1590         }
1591         return EMPTY_BYTE_ARRAY;
1592     }
1593 
1594     private void lowerIncDecUnaryOperator(OperatorKind kind, boolean isPrefix, IRNode node) {
1595         IRNode var = node.getChild(0);
1596         Literal one;
1597         Type resultType = node.getResultType();
1598         if (resultType.equals(TypeList.LONG)) {
1599             one = new Literal(1L, TypeList.LONG);
1600         } else if (resultType.equals(TypeList.INT)) {
1601             one = new Literal(1, TypeList.INT);
1602         } else if (resultType.equals(TypeList.SHORT)) {
1603             one = new Literal((short) 1, TypeList.SHORT);
1604         } else {
1605             one = new Literal((byte) 1, TypeList.BYTE);
1606         }
1607         if (var instanceof NonStaticMemberVariable) {
1608             IRNode holder = var.getChild(0);
1609             Type type = holder.getResultType();
1610             VariableInfo tmpInfo = new VariableInfo("tmpObject_" + tmpObject++,
1611                     currentClass, type, VariableInfo.LOCAL);
1612             new Statement(new VariableInitialization(tmpInfo, holder), true).accept(this);
1613             var = new NonStaticMemberVariable(new LocalVariable(tmpInfo),
1614                     ((NonStaticMemberVariable) var).getVariableInfo());
1615         }
1616         BinaryOperator calculation = new BinaryOperator(kind, resultType, var, one);
1617         BinaryOperator changeValue = new BinaryOperator(OperatorKind.ASSIGN, resultType, var, calculation);
1618         Statement finalChangeStatement = new Statement(changeValue, true);
1619         if (isPrefix) {
1620             finalChangeStatement.accept(this);
1621             var.accept(this);
1622         } else {
1623             var.accept(this);
1624             finalChangeStatement.accept(this);
1625         }
1626     }
1627 
1628     @Override
1629     public byte[] visit(VariableDeclaration node) {
1630         VariableInfo vi = node.getVariableInfo();
1631         String ownerName = vi.getOwner().getName();
1632         ContextDependedClassWriter cw = classWriters.get(ownerName);
1633         String typeName = new String(vi.type.accept(this));
1634         if (vi.isLocal()) {
1635             locals.addLocal(vi);
1636         } else {
1637             FieldVisitor fv = cw.visitField(asAccessFlags(vi),
1638                     vi.name,
1639                     typeName,
1640                     null /* Generic */,
1641                     null /* Constant value */);
1642             fv.visitEnd(); // doesn't need visitAnnotation and visitAttribute
1643         }
1644         return EMPTY_BYTE_ARRAY;
1645     }
1646 
1647     @Override
1648     public byte[] visit(VariableDeclarationBlock node) {
1649         return iterateBlock(node);
1650     }
1651 
1652     @Override
1653     public byte[] visit(While node) {
1654         Loop loop = node.getLoop();
1655         loop.initialization.accept(this);
1656         node.getChild(While.WhilePart.HEADER.ordinal()).accept(this);
1657         Label currentLoopBegin = new Label();
1658         beginLabels.push(currentLoopBegin);
1659         currentMV.visitLabel(currentLoopBegin);
1660         loop.condition.accept(this);
1661         assert loop.condition.getResultType() == TypeList.BOOLEAN;
1662         Label currentLoopEnd = new Label();
1663         endLabels.push(currentLoopEnd);
1664         currentMV.visitJumpInsn(Opcodes.IFEQ, currentLoopEnd);
1665         node.getChild(While.WhilePart.BODY1.ordinal()).accept(this);
1666         loop.manipulator.accept(this);
1667         node.getChild(While.WhilePart.BODY2.ordinal()).accept(this);
1668         node.getChild(While.WhilePart.BODY3.ordinal()).accept(this);
1669         currentMV.visitJumpInsn(Opcodes.GOTO, currentLoopBegin);
1670         currentMV.visitLabel(currentLoopEnd);
1671         Label a = beginLabels.pop();
1672         assert currentLoopBegin == a;
1673         a = endLabels.pop();
1674         assert currentLoopEnd == a;
1675         return EMPTY_BYTE_ARRAY;
1676     }
1677 
1678     public byte[] getByteCode(String name) {
1679         return context.get(name);
1680     }
1681 
1682     private static byte[] concat(byte[] a, byte[] b) {
1683         byte[] r = new byte[a.length + b.length];
1684         System.arraycopy(a, 0, r, 0, a.length);
1685         System.arraycopy(b, 0, r, a.length, b.length);
1686         return r;
1687     }
1688 
1689     private String argTypeToString(ArgumentDeclaration declarations) {
1690         return new String(declarations.variableInfo.type.accept(this));
1691     }
1692 
1693     private byte[] iterateBlock(IRNode node) {
1694         return node.getChildren().stream()
1695                 .map(ch -> ch.accept(this))
1696                 .reduce(new byte[0], ByteCodeVisitor::concat);
1697     }
1698 
1699     private String getDescriptor(IRNode node, int skipChilds, String returnType) {
1700         return node.getChildren().stream()
1701                 .skip(skipChilds)
1702                 .map(c -> argTypeToString((ArgumentDeclaration)c))
1703                 .collect(Collectors.joining("", "(", ")" + returnType));
1704     }
1705 
1706     private static String asInternalName(String type) {
1707         return type.replace('.', '/');
1708     }
1709 
1710     private static int asAccessFlags(TypeKlass klass) {
1711         int attr = Opcodes.ACC_SUPER;
1712         attr |= klass.isFinal() ? Opcodes.ACC_FINAL : 0;
1713         attr |= klass.isAbstract() ? Opcodes.ACC_ABSTRACT : 0;
1714         attr |= klass.isInterface() ? Opcodes.ACC_INTERFACE : 0;
1715 
1716         return attr;
1717     }
1718 
1719     private static int asAccessFlags(FunctionInfo fi) {
1720         int result = asAccessFlags((Symbol) fi);
1721         result |= ProductionParams.enableStrictFP.value() ? Opcodes.ACC_STRICT : 0;
1722         result |= fi.isSynchronized() ? Opcodes.ACC_SYNCHRONIZED : 0;
1723         return result;
1724     }
1725 
1726     private static int asAccessFlags(Symbol s) {
1727         int attr = 0;
1728         attr |= s.isPublic() ? Opcodes.ACC_PUBLIC : 0;
1729         attr |= s.isPrivate() ? Opcodes.ACC_PRIVATE : 0;
1730         attr |= s.isProtected() ? Opcodes.ACC_PROTECTED : 0;
1731         attr |= s.isStatic() ? Opcodes.ACC_STATIC : 0;
1732         attr |= s.isFinal() ? Opcodes.ACC_FINAL : 0;
1733 
1734         return attr;
1735     }
1736 
1737     private static class LocalVariablesTable {
1738         private int nextLocalIndex = 0;
1739         // a map keeping local variable table index for a local variable
1740         private final HashMap<String, Integer> locals = new HashMap<>();
1741 
1742         public int addLocal(VariableInfo vi) {
1743             int indexToReturn = nextLocalIndex;
1744             locals.put(vi.name, nextLocalIndex++);
1745             if (vi.type.equals(TypeList.DOUBLE) || vi.type.equals(TypeList.LONG)) {
1746                 nextLocalIndex++;
1747             }
1748             return indexToReturn;
1749         }
1750 
1751         public int getLocalIndex(VariableInfo vi) {
1752             if (!locals.containsKey(vi.name)) {
1753                 throw new NoSuchElementException(vi.name);
1754             }
1755             return locals.get(vi.name);
1756         }
1757 
1758         public void clear() {
1759             locals.clear();
1760             nextLocalIndex = 0;
1761         }
1762 
1763         public void initFunctionArguments(FunctionInfo info) {
1764             initArguments(null, info);
1765         }
1766 
1767         public void initConstructorArguments(TypeKlass owner, FunctionInfo info) {
1768             Objects.requireNonNull(owner, "owner is null");
1769             initArguments(owner, info);
1770         }
1771 
1772         private void initArguments(TypeKlass owner, FunctionInfo info) {
1773             clear();
1774             if (owner != null) {
1775                 addLocal(new VariableInfo("this", owner, owner, VariableInfo.LOCAL | VariableInfo.INITIALIZED));
1776             }
1777             for (VariableInfo vi : info.argTypes) {
1778                 addLocal(vi);
1779             }
1780         }
1781     }
1782 
1783     private static class GeneratedClassesContext extends java.lang.ClassLoader {
1784         private final HashMap<String, byte[]> byteCodes = new HashMap<>();
1785 
1786         public void register(String name, byte[] bytecode) {
1787             defineClass(name, bytecode, 0, bytecode.length);
1788             byteCodes.put(name, bytecode);
1789         }
1790 
1791         public byte[] get(String name) {
1792             return byteCodes.get(name);
1793         }
1794     }
1795 
1796 
1797     private static class ContextDependedClassWriter extends ClassWriter {
1798         private final GeneratedClassesContext context;
1799 
1800         public ContextDependedClassWriter(GeneratedClassesContext context, int flags) {
1801             super(flags);
1802             this.context = context;
1803         }
1804 
1805         protected String getCommonSuperClass(String className1, String className2) {
1806             Class<?> klass1;
1807             Class<?> klass2;
1808             try {
1809                 klass1 = Class.forName(className1.replace('/', '.'), false, context);
1810                 klass2 = Class.forName(className2.replace('/', '.'), false, context);
1811             } catch (ClassNotFoundException e) {
1812                 throw new Error("can not get common supper for " + className1
1813                                 + " and " + className2, e);
1814             }
1815 
1816             if (klass1.isAssignableFrom(klass2)) {
1817                 return className1;
1818             } else if (klass2.isAssignableFrom(klass1)) {
1819                 return className2;
1820             } else if (!klass1.isInterface() && !klass2.isInterface()) {
1821                 do {
1822                     klass1 = klass1.getSuperclass();
1823                 } while (!klass1.isAssignableFrom(klass2));
1824 
1825                 return asInternalName(className1);
1826             } else {
1827                 return "java/lang/Object";
1828             }
1829         }
1830     }
1831 }