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
24package jdk.test.lib.jittester.visitors;
25
26import java.util.ArrayDeque;
27import java.util.Deque;
28import java.util.HashMap;
29import java.util.List;
30import java.util.NoSuchElementException;
31import java.util.Objects;
32import java.util.SortedMap;
33import java.util.TreeMap;
34import java.util.stream.Collectors;
35import java.util.stream.Stream;
36
37import jdk.internal.org.objectweb.asm.ClassWriter;
38import jdk.internal.org.objectweb.asm.FieldVisitor;
39import jdk.internal.org.objectweb.asm.Label;
40import jdk.internal.org.objectweb.asm.MethodVisitor;
41import jdk.internal.org.objectweb.asm.Opcodes;
42import jdk.test.lib.util.Pair;
43import jdk.test.lib.jittester.BinaryOperator;
44import jdk.test.lib.jittester.Block;
45import jdk.test.lib.jittester.BuiltInType;
46import jdk.test.lib.jittester.Break;
47import jdk.test.lib.jittester.CastOperator;
48import jdk.test.lib.jittester.CatchBlock;
49import jdk.test.lib.jittester.Continue;
50import jdk.test.lib.jittester.Declaration;
51import jdk.test.lib.jittester.IRNode;
52import jdk.test.lib.jittester.If;
53import jdk.test.lib.jittester.Initialization;
54import jdk.test.lib.jittester.Literal;
55import jdk.test.lib.jittester.LocalVariable;
56import jdk.test.lib.jittester.NonStaticMemberVariable;
57import jdk.test.lib.jittester.Nothing;
58import jdk.test.lib.jittester.Operator;
59import jdk.test.lib.jittester.OperatorKind;
60import jdk.test.lib.jittester.PrintVariables;
61import jdk.test.lib.jittester.ProductionParams;
62import jdk.test.lib.jittester.Statement;
63import jdk.test.lib.jittester.StaticMemberVariable;
64import jdk.test.lib.jittester.Switch;
65import jdk.test.lib.jittester.Symbol;
66import jdk.test.lib.jittester.TernaryOperator;
67import jdk.test.lib.jittester.Throw;
68import jdk.test.lib.jittester.TryCatchBlock;
69import jdk.test.lib.jittester.Type;
70import jdk.test.lib.jittester.TypeList;
71import jdk.test.lib.jittester.UnaryOperator;
72import jdk.test.lib.jittester.VariableBase;
73import jdk.test.lib.jittester.VariableDeclaration;
74import jdk.test.lib.jittester.VariableDeclarationBlock;
75import jdk.test.lib.jittester.VariableInfo;
76import jdk.test.lib.jittester.VariableInitialization;
77import jdk.test.lib.jittester.arrays.ArrayCreation;
78import jdk.test.lib.jittester.arrays.ArrayElement;
79import jdk.test.lib.jittester.arrays.ArrayExtraction;
80import jdk.test.lib.jittester.classes.ClassDefinitionBlock;
81import jdk.test.lib.jittester.classes.Interface;
82import jdk.test.lib.jittester.classes.Klass;
83import jdk.test.lib.jittester.classes.MainKlass;
84import jdk.test.lib.jittester.functions.ArgumentDeclaration;
85import jdk.test.lib.jittester.functions.ConstructorDefinition;
86import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock;
87import jdk.test.lib.jittester.functions.Function;
88import jdk.test.lib.jittester.functions.FunctionDeclaration;
89import jdk.test.lib.jittester.functions.FunctionDeclarationBlock;
90import jdk.test.lib.jittester.functions.FunctionDefinition;
91import jdk.test.lib.jittester.functions.FunctionDefinitionBlock;
92import jdk.test.lib.jittester.functions.FunctionInfo;
93import jdk.test.lib.jittester.functions.FunctionRedefinition;
94import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock;
95import jdk.test.lib.jittester.functions.Return;
96import jdk.test.lib.jittester.functions.StaticConstructorDefinition;
97import jdk.test.lib.jittester.loops.CounterInitializer;
98import jdk.test.lib.jittester.loops.CounterManipulator;
99import jdk.test.lib.jittester.loops.DoWhile;
100import jdk.test.lib.jittester.loops.For;
101import jdk.test.lib.jittester.loops.Loop;
102import jdk.test.lib.jittester.loops.LoopingCondition;
103import jdk.test.lib.jittester.loops.While;
104import jdk.test.lib.jittester.types.TypeArray;
105import jdk.test.lib.jittester.types.TypeKlass;
106import jdk.test.lib.jittester.utils.FixedTrees;
107import jdk.test.lib.jittester.utils.PseudoRandom;
108
109public 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}
1832