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.util.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
visit(ArgumentDeclaration node)121     public byte[] visit(ArgumentDeclaration node) {
122         /* handled by FunctionDefinition for ByteCodeVisitor */
123         return EMPTY_BYTE_ARRAY;
124     }
125 
126     @Override
visit(ArrayCreation node)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
visit(ArrayElement node)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
visit(ArrayExtraction node)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
visit(BinaryOperator node)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;
lowerCompoundBinaryOperator(OperatorKind kind, IRNode node)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 
generateBasicLogicOperator(int ifOpcode, boolean retTrueFirst, IRNode left, IRNode right)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 
generateCmpBasedCode(Type type, int nonIntOpcode, int intOpcode)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      */
convertTopType(Type from, Type to)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
visit(Block node)639     public byte[] visit(Block node) {
640         return iterateBlock(node);
641     }
642 
643     @Override
visit(Break node)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
visit(CastOperator node)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
visit(CatchBlock node)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
visit(ClassDefinitionBlock node)680     public byte[] visit(ClassDefinitionBlock node) {
681         return iterateBlock(node);
682     }
683 
684     @Override
visit(ConstructorDefinition node)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 
generateDataMembers(IRNode node)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
visit(ConstructorDefinitionBlock node)716     public byte[] visit(ConstructorDefinitionBlock node) {
717         return iterateBlock(node);
718     }
719 
720     @Override
visit(Continue node)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
visit(CounterInitializer node)730     public byte[] visit(CounterInitializer node) {
731         visitLocalVar(node);
732         emitPop(node.getVariableInfo().type);
733         return EMPTY_BYTE_ARRAY;
734     }
735 
visitLocalVar(Initialization node)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
visit(CounterManipulator node)758     public byte[] visit(CounterManipulator node) {
759         return node.getChild(0).accept(this);
760     }
761 
762     @Override
visit(Declaration node)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
visit(DoWhile node)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
visit(For node)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
visit(Function node)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
visit(FunctionDeclaration node)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
visit(FunctionDeclarationBlock node)877     public byte[] visit(FunctionDeclarationBlock node) {
878         return iterateBlock(node);
879     }
880 
881     @Override
visit(FunctionDefinition node)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
visit(FunctionDefinitionBlock node)901     public byte[] visit(FunctionDefinitionBlock node) {
902         return iterateBlock(node);
903     }
904 
905     @Override
visit(FunctionRedefinition node)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
visit(FunctionRedefinitionBlock node)924     public byte[] visit(FunctionRedefinitionBlock node) {
925         return iterateBlock(node);
926     }
927 
928     @Override
visit(If node)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      */
generateIf(int ifOpcode, IRNode thenBlock, IRNode elseBlock)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
visit(Initialization node)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 
emitDupX1(Type type)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 
emitDup(Type type)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
visit(Interface node)1012     public byte[] visit(Interface node) {
1013         String name = node.getName();
1014         ContextDependedClassWriter classWriter = new ContextDependedClassWriter(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
visit(Klass node)1035     public byte[] visit(Klass node) {
1036         String name = node.getName();
1037         TypeKlass prevClass = currentClass;
1038         currentClass = node.getThisKlass();
1039         ContextDependedClassWriter classWriter = new ContextDependedClassWriter(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 
visitLiteral(boolean value)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 
visitLiteral(byte value)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 
visitLiteral(short value)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 
visitLiteral(char value)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 
visitLiteral(int value)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 
visitLiteral(long value)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 
visitLiteral(float value)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 
visitLiteral(double value)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
visit(Literal node)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
visit(LocalVariable node)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
visit(LoopingCondition node)1227     public byte[] visit(LoopingCondition node) {
1228         return node.getCondition().accept(this);
1229     }
1230 
1231     @Override
visit(MainKlass node)1232     public byte[] visit(MainKlass node) {
1233         TypeKlass prevClass = currentClass;
1234         currentClass = node.getThisKlass();
1235         String name = node.getName();
1236         ContextDependedClassWriter mainClassWriter = new ContextDependedClassWriter(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
visit(NonStaticMemberVariable node)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
visit(Nothing node)1300     public byte[] visit(Nothing node) {
1301         // TODO : add randomness
1302         currentMV.visitInsn(Opcodes.NOP);
1303         return EMPTY_BYTE_ARRAY;
1304     }
1305 
1306     @Override
visit(PrintVariables node)1307     public byte[] visit(PrintVariables node) {
1308         return FixedTrees.printVariablesAsFunction(node).accept(this);
1309     }
1310 
1311     @Override
visit(Return node)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
visit(Statement node)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 
emitPop(Type resultType)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
visit(StaticConstructorDefinition node)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
visit(StaticMemberVariable node)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
visit(Switch node)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
visit(TernaryOperator node)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
visit(Throw node)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
visit(TryCatchBlock node)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
visit(Type node)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
visit(TypeArray node)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
visit(UnaryOperator node)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 
lowerIncDecUnaryOperator(OperatorKind kind, boolean isPrefix, IRNode node)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
visit(VariableDeclaration node)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
visit(VariableDeclarationBlock node)1648     public byte[] visit(VariableDeclarationBlock node) {
1649         return iterateBlock(node);
1650     }
1651 
1652     @Override
visit(While node)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 
getByteCode(String name)1678     public byte[] getByteCode(String name) {
1679         return context.get(name);
1680     }
1681 
concat(byte[] a, byte[] b)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 
argTypeToString(ArgumentDeclaration declarations)1689     private String argTypeToString(ArgumentDeclaration declarations) {
1690         return new String(declarations.variableInfo.type.accept(this));
1691     }
1692 
iterateBlock(IRNode node)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 
getDescriptor(IRNode node, int skipChilds, String returnType)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 
asInternalName(String type)1706     private static String asInternalName(String type) {
1707         return type.replace('.', '/');
1708     }
1709 
asAccessFlags(TypeKlass klass)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 
asAccessFlags(FunctionInfo fi)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 
asAccessFlags(Symbol s)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 
addLocal(VariableInfo vi)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 
getLocalIndex(VariableInfo vi)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 
clear()1758         public void clear() {
1759             locals.clear();
1760             nextLocalIndex = 0;
1761         }
1762 
initFunctionArguments(FunctionInfo info)1763         public void initFunctionArguments(FunctionInfo info) {
1764             initArguments(null, info);
1765         }
1766 
initConstructorArguments(TypeKlass owner, FunctionInfo info)1767         public void initConstructorArguments(TypeKlass owner, FunctionInfo info) {
1768             Objects.requireNonNull(owner, "owner is null");
1769             initArguments(owner, info);
1770         }
1771 
initArguments(TypeKlass owner, FunctionInfo info)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 {
1784         private final HashMap<String, byte[]> byteCodes = new HashMap<>();
1785 
register(String name, byte[] bytecode)1786         public void register(String name, byte[] bytecode) {
1787             byteCodes.put(name, bytecode);
1788         }
1789 
get(String name)1790         public byte[] get(String name) {
1791             return byteCodes.get(name);
1792         }
1793     }
1794 
1795 
1796     private static class ContextDependedClassWriter extends ClassWriter {
ContextDependedClassWriter(int flags)1797         public ContextDependedClassWriter(int flags) {
1798             super(flags);
1799         }
1800 
getCommonSuperClass(String className1, String className2)1801         protected String getCommonSuperClass(String className1, String className2) {
1802             TypeKlass type1 = (TypeKlass) TypeList.find(className1.replace('/', '.'));
1803             TypeKlass type2 = (TypeKlass) TypeList.find(className2.replace('/', '.'));
1804             if (type1 == null || type2 == null) {
1805                 return super.getCommonSuperClass(className1, className2);
1806             }
1807 
1808             if (type2.canImplicitlyCastTo(type1)) {
1809                 return className1;
1810             }
1811             if (type1.canImplicitlyCastTo(type2)) {
1812                 return className2;
1813             }
1814             if (type1.isInterface() || type2.isInterface()) {
1815                 return "java/lang/Object";
1816             }
1817 
1818             do {
1819                 type1 = type1.getParent();
1820             } while (!type2.canImplicitlyCastTo(type1));
1821 
1822             return asInternalName(type1.getName());
1823         }
1824     }
1825 }
1826