1/*
2 * Copyright (c) 2015, 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.List;
27import java.util.Locale;
28import java.util.Objects;
29import java.util.stream.Collectors;
30import jdk.test.lib.jittester.BinaryOperator;
31import jdk.test.lib.jittester.Block;
32import jdk.test.lib.jittester.Break;
33import jdk.test.lib.jittester.CastOperator;
34import jdk.test.lib.jittester.CatchBlock;
35import jdk.test.lib.jittester.Continue;
36import jdk.test.lib.jittester.Declaration;
37import jdk.test.lib.jittester.IRNode;
38import jdk.test.lib.jittester.If;
39import jdk.test.lib.jittester.Initialization;
40import jdk.test.lib.jittester.Literal;
41import jdk.test.lib.jittester.LocalVariable;
42import jdk.test.lib.jittester.NonStaticMemberVariable;
43import jdk.test.lib.jittester.Nothing;
44import jdk.test.lib.jittester.Operator;
45import jdk.test.lib.jittester.OperatorKind;
46import jdk.test.lib.jittester.PrintVariables;
47import jdk.test.lib.jittester.ProductionParams;
48import jdk.test.lib.jittester.Statement;
49import jdk.test.lib.jittester.StaticMemberVariable;
50import jdk.test.lib.jittester.Switch;
51import jdk.test.lib.jittester.Symbol;
52import jdk.test.lib.jittester.TernaryOperator;
53import jdk.test.lib.jittester.Throw;
54import jdk.test.lib.jittester.TryCatchBlock;
55import jdk.test.lib.jittester.Type;
56import jdk.test.lib.jittester.TypeList;
57import jdk.test.lib.jittester.UnaryOperator;
58import jdk.test.lib.jittester.VariableBase;
59import jdk.test.lib.jittester.VariableDeclaration;
60import jdk.test.lib.jittester.VariableDeclarationBlock;
61import jdk.test.lib.jittester.VariableInfo;
62import jdk.test.lib.jittester.arrays.ArrayCreation;
63import jdk.test.lib.jittester.arrays.ArrayElement;
64import jdk.test.lib.jittester.arrays.ArrayExtraction;
65import jdk.test.lib.jittester.classes.ClassDefinitionBlock;
66import jdk.test.lib.jittester.classes.Interface;
67import jdk.test.lib.jittester.classes.Klass;
68import jdk.test.lib.jittester.classes.MainKlass;
69import jdk.test.lib.jittester.functions.ArgumentDeclaration;
70import jdk.test.lib.jittester.functions.ConstructorDefinition;
71import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock;
72import jdk.test.lib.jittester.functions.Function;
73import jdk.test.lib.jittester.functions.FunctionDeclaration;
74import jdk.test.lib.jittester.functions.FunctionDeclarationBlock;
75import jdk.test.lib.jittester.functions.FunctionDefinition;
76import jdk.test.lib.jittester.functions.FunctionDefinitionBlock;
77import jdk.test.lib.jittester.functions.FunctionInfo;
78import jdk.test.lib.jittester.functions.FunctionRedefinition;
79import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock;
80import jdk.test.lib.jittester.functions.Return;
81import jdk.test.lib.jittester.functions.StaticConstructorDefinition;
82import jdk.test.lib.jittester.loops.CounterInitializer;
83import jdk.test.lib.jittester.loops.CounterManipulator;
84import jdk.test.lib.jittester.loops.DoWhile;
85import jdk.test.lib.jittester.loops.For;
86import jdk.test.lib.jittester.loops.Loop;
87import jdk.test.lib.jittester.loops.LoopingCondition;
88import jdk.test.lib.jittester.loops.While;
89import jdk.test.lib.jittester.types.TypeArray;
90import jdk.test.lib.jittester.types.TypeKlass;
91import jdk.test.lib.jittester.utils.FixedTrees;
92import jdk.test.lib.jittester.utils.PrintingUtils;
93
94public class JavaCodeVisitor implements Visitor<String> {
95
96    public static String funcAttributes(FunctionInfo fi) {
97        String attrs = attributes(fi);
98        if (fi.isSynchronized()) {
99            attrs += "synchronized ";
100        }
101        return attrs;
102    }
103
104    public static String attributes(Symbol s) {
105        String attrs = "";
106        if (s.isPrivate()) {
107            attrs += "private ";
108        }
109        if (s.isProtected()) {
110            attrs += "protected ";
111        }
112        if (s.isPublic()) {
113            attrs += "public ";
114        }
115        if (s.isFinal()) {
116            attrs += "final ";
117        }
118        if (s.isStatic()) {
119            attrs += "static ";
120        }
121        return attrs;
122    }
123
124    private String operatorToJaveCode(OperatorKind operationKind) {
125       switch (operationKind) {
126           case COMPOUND_ADD:
127               return  "+=";
128           case COMPOUND_SUB:
129               return "-=";
130           case COMPOUND_MUL:
131               return "*=";
132           case COMPOUND_DIV:
133               return "/=";
134           case COMPOUND_MOD:
135               return "%=";
136           case COMPOUND_AND:
137               return "&=";
138           case COMPOUND_OR:
139               return "|=";
140           case COMPOUND_XOR:
141               return "^=";
142           case COMPOUND_SHR:
143               return ">>=";
144           case COMPOUND_SHL:
145               return "<<=";
146           case COMPOUND_SAR:
147               return ">>>=";
148           case ASSIGN:
149               return "=";
150           case OR:
151               return "||";
152           case BIT_OR:
153               return "|";
154           case BIT_XOR:
155               return "^";
156           case AND:
157               return "&&";
158           case BIT_AND:
159               return "&";
160           case EQ:
161               return "==";
162           case NE:
163               return "!=";
164           case GT:
165               return ">";
166           case LT:
167               return "<";
168           case GE:
169               return ">=";
170           case LE:
171               return "<=";
172           case SHR:
173               return ">>";
174           case SHL:
175               return "<<";
176           case SAR:
177               return ">>>";
178           case ADD:
179           case STRADD:
180               return "+";
181           case SUB:
182               return "-";
183           case MUL:
184               return "*";
185           case DIV:
186               return "/";
187           case MOD:
188               return "%";
189           case NOT:
190               return "!";
191           case BIT_NOT:
192               return "~";
193           case UNARY_PLUS:
194               return "+";
195           case UNARY_MINUS:
196               return "-";
197           case PRE_DEC:
198           case POST_DEC:
199               return "--";
200           case PRE_INC:
201           case POST_INC:
202               return "++";
203           default:
204               throw new IllegalArgumentException("Unkown operator kind " + operationKind);
205       }
206    }
207
208    private String expressionToJavaCode(Operator t, IRNode p, Operator.Order o) {
209        String result;
210        try {
211            if ((o == Operator.Order.LEFT && ((Operator) p).getPriority() < t.getPriority())
212                    || (o == Operator.Order.RIGHT && ((Operator) p).getPriority() <= t.getPriority())) {
213                result = "(" + p.accept(this)+ ")";
214            } else {
215                result = p.accept(this);
216            }
217        } catch (Exception e) {
218            result = p.accept(this);
219        }
220        return result;
221    }
222
223    @Override
224    public String visit(ArgumentDeclaration node) {
225        VariableInfo vi = node.variableInfo;
226        return attributes(vi) + vi.type.accept(this) + " " + vi.name;
227    }
228
229    @Override
230    public String visit(ArrayCreation node) {
231        Type arrayElemType = node.getArrayType().type;
232        String type = arrayElemType.accept(this);
233        String name = node.getVariable().getName();
234        StringBuilder code = new StringBuilder()
235                .append(node.getVariable().accept(this))
236                .append(";\n")
237                .append(PrintingUtils.align(node.getParent().getLevel()))
238                .append(name)
239                .append(" = new ")
240                .append(type);
241        code.append(node.getChildren().stream()
242                .map(p -> p.accept(this))
243                .collect(Collectors.joining("][", "[", "]")));
244        code.append(";\n");
245        if (!TypeList.isBuiltIn(arrayElemType)) {
246            code.append(PrintingUtils.align(node.getParent().getLevel()))
247                .append("java.util.Arrays.fill(")
248                .append(name)
249                .append(", new ")
250                .append(type)
251                .append("());\n");
252        }
253        return code.toString();
254    }
255
256    @Override
257    public String visit(ArrayElement node) {
258        IRNode array = node.getChild(0);
259        StringBuilder code = new StringBuilder();
260        if (array instanceof VariableBase || array instanceof Function) {
261            code.append(array.accept(this));
262        } else {
263            code.append("(")
264                .append(array.accept(this))
265                .append(")");
266        }
267        code.append(node.getChildren().stream()
268                .skip(1)
269                .map(c -> c.accept(this))
270                .collect(Collectors.joining("][", "[", "]")));
271        return code.toString();
272    }
273
274    @Override
275    public String visit(ArrayExtraction node) {
276        IRNode array = node.getChild(0);
277        StringBuilder code = new StringBuilder();
278        if (array instanceof VariableBase || array instanceof Function) {
279            code.append(array.accept(this));
280        } else {
281            code.append("(")
282                .append(array.accept(this))
283                .append(")");
284        }
285        code.append(node.getChildren().stream()
286                .skip(1)
287                .map(c -> c.accept(this))
288                .collect(Collectors.joining("][", "[", "]")));
289        return code.toString();
290    }
291
292    @Override
293    public String visit(BinaryOperator node) {
294        IRNode left = node.getChild(Operator.Order.LEFT.ordinal());
295        IRNode right = node.getChild(Operator.Order.RIGHT.ordinal());
296        if (left == null || right == null) {
297            return "null";
298        }
299        return expressionToJavaCode(node, left, Operator.Order.LEFT)
300               + " " + operatorToJaveCode(node.getOperationKind()) + " "
301               + expressionToJavaCode(node, right, Operator.Order.RIGHT);
302    }
303
304    @Override
305    public String visit(Block node) {
306        StringBuilder code = new StringBuilder();
307        for (IRNode i : node.getChildren()) {
308            String s = i.accept(this);
309            if (!s.isEmpty()) {
310                int level = node.getLevel();
311                if (i instanceof Block) {
312                    code.append(PrintingUtils.align(level + 1))
313                        .append("{\n")
314                        .append(s)
315                        .append(PrintingUtils.align(level + 1))
316                        .append("}");
317                } else {
318                    code.append(PrintingUtils.align(level + 1))
319                        .append(s);
320                }
321                code.append(addComplexityInfo(i));
322                code.append("\n");
323            }
324        }
325        return code.toString();
326    }
327
328    private String addComplexityInfo(IRNode node) {
329        if (ProductionParams.printComplexity.value()) {
330            return " /* " + node.complexity() + " */";
331        }
332        return "";
333    }
334
335    @Override
336    public String visit(Break node) {
337        return "break;";
338    }
339
340    @Override
341    public String visit(CastOperator node) {
342        return "(" + node.getResultType().accept(this)+ ")"
343                + expressionToJavaCode(node, node.getChild(0), Operator.Order.LEFT);
344    }
345
346    @Override
347    public String visit(ClassDefinitionBlock node) {
348        StringBuilder code = new StringBuilder();
349        for (IRNode i : node.getChildren()) {
350            code.append("\n")
351                .append(PrintingUtils.align(node.getLevel()))
352                .append(i.accept(this))
353                .append("\n");
354        }
355
356        return code.toString();
357    }
358
359    @Override
360    public String visit(ConstructorDefinition node) {
361        String args = node.getChildren().stream()
362                .skip(1)
363                .map(c -> c.accept(this))
364                .collect(Collectors.joining(", "));
365        IRNode body = node.getChild(0);
366        StringBuilder code = new StringBuilder();
367        code.append(funcAttributes(node.getFunctionInfo()))
368            .append(node.getFunctionInfo().name)
369            .append("(")
370            .append(args)
371            .append(")\n")
372            .append(PrintingUtils.align(node.getLevel() + 1))
373            .append("{\n")
374            .append(body != null ? body.accept(this) : "")
375            .append(PrintingUtils.align(node.getLevel() + 1))
376            .append("}");
377        return code.toString();
378    }
379
380    @Override
381    public String visit(ConstructorDefinitionBlock node) {
382        StringBuilder code = new StringBuilder();
383        for (IRNode i : node.getChildren()) {
384            code.append("\n")
385                .append(PrintingUtils.align(node.getLevel()))
386                .append(i.accept(this))
387                .append(addComplexityInfo(i))
388                .append("\n");
389        }
390        return code.toString();
391    }
392
393    @Override
394    public String visit(Continue node) {
395        return "continue;";
396    }
397
398    @Override
399    public String visit(CounterInitializer node) {
400        VariableInfo vi = node.getVariableInfo();
401        return vi.type.accept(this) + " " + vi.name + " = " + node.getChild(0).accept(this)+ ";";
402    }
403
404    @Override
405    public String visit(CounterManipulator node) {
406        return node.getChild(0).accept(this);
407    }
408
409    @Override
410    public String visit(Declaration node) {
411        return node.getChild(0).accept(this)+ ";";
412    }
413
414    @Override
415    public String visit(DoWhile node) {
416        IRNode header = node.getChild(DoWhile.DoWhilePart.HEADER.ordinal());
417        IRNode body1 = node.getChild(DoWhile.DoWhilePart.BODY1.ordinal());
418        IRNode body2 = node.getChild(DoWhile.DoWhilePart.BODY2.ordinal());
419        StringBuilder code = new StringBuilder();
420        Loop loop = node.getLoop();
421        int level = node.getLevel();
422        code.append(loop.initialization.accept(this))
423            .append("\n")
424            .append(header.accept(this))
425            .append(PrintingUtils.align(level))
426            .append("do\n")
427            .append(PrintingUtils.align(level))
428            .append("{\n")
429            .append(body1.accept(this))
430            .append(PrintingUtils.align(level + 1))
431            .append(loop.manipulator.accept(this))
432            .append(";\n")
433            .append(body2.accept(this))
434            .append(PrintingUtils.align(level))
435            .append("} while (")
436            .append(loop.condition.accept(this))
437            .append(");");
438        return code.toString();
439    }
440
441    @Override
442    public String visit(For node) {
443        IRNode header = node.getChild(For.ForPart.HEADER.ordinal());
444        IRNode statement1 = node.getChild(For.ForPart.STATEMENT1.ordinal());
445        IRNode statement2 = node.getChild(For.ForPart.STATEMENT2.ordinal());
446        IRNode body1 = node.getChild(For.ForPart.BODY1.ordinal());
447        IRNode body2 = node.getChild(For.ForPart.BODY2.ordinal());
448        IRNode body3 = node.getChild(For.ForPart.BODY3.ordinal());
449        Loop loop = node.getLoop();
450        StringBuilder code = new StringBuilder();
451        int level = node.getLevel();
452        code.append(loop.initialization.accept(this))
453            .append("\n")
454            .append(header.accept(this))
455            .append(PrintingUtils.align(level))
456            .append("for (")
457            .append(statement1.accept(this))
458            .append("; ")
459            .append(loop.condition.accept(this))
460            .append("; ")
461            .append(statement2.accept(this))
462            .append(")\n")
463            .append(PrintingUtils.align(level))
464            .append("{\n")
465            .append(body1.accept(this))
466            .append(PrintingUtils.align(level + 1))
467            .append(loop.manipulator.accept(this))
468            .append(";\n")
469            .append(body2.accept(this))
470            .append(body3.accept(this))
471            .append(PrintingUtils.align(level))
472            .append("}");
473        return code.toString();
474    }
475
476    @Override
477    public String visit(Function node) {
478        FunctionInfo value = node.getValue();
479        String nameAndArgs = value.name + "("
480                + node.getChildren().stream()
481                    .skip(value.isStatic() || value.isConstructor() ? 0 : 1)
482                    .map(c -> c.accept(this))
483                    .collect(Collectors.joining(", "))
484                + ")";
485        String prefix = "";
486        if (value.isStatic()) {
487            if(!node.getOwner().equals(value.owner)) {
488                prefix = value.owner.getName() + ".";
489            }
490        } else if (value.isConstructor()) {
491            prefix = "new ";
492        } else {
493            IRNode object = node.getChild(0);
494            String objectString = object.accept(this);
495            if (!objectString.equals("this")) {
496                 if (object instanceof VariableBase || object instanceof Function
497                            || object instanceof Literal) {
498                     prefix = objectString + ".";
499                 } else {
500                     prefix = "(" + objectString + ")" + ".";
501                 }
502            }
503        }
504        return prefix + nameAndArgs;
505    }
506
507    @Override
508    public String visit(FunctionDeclaration node) {
509        String args = node.getChildren().stream()
510                .map(c -> c.accept(this))
511                .collect(Collectors.joining(", "));
512
513        FunctionInfo functionInfo = node.getFunctionInfo();
514        return (functionInfo.owner.isInterface() ? "" : "abstract ")
515                + funcAttributes(functionInfo) + functionInfo.type.accept(this)+ " "
516                + functionInfo.name + "(" + args + ");";
517    }
518
519    @Override
520    public String visit(FunctionDeclarationBlock node) {
521        StringBuilder code = new StringBuilder();
522        for (IRNode i : node.getChildren()) {
523            code.append(PrintingUtils.align(node.getLevel()))
524                .append(i.accept(this))
525                .append(addComplexityInfo(i))
526                .append("\n");
527        }
528        return code.toString();
529    }
530
531    @Override
532    public String visit(FunctionDefinition node) {
533        String args = node.getChildren().stream()
534                .skip(2)
535                .map(c -> c.accept(this))
536                .collect(Collectors.joining(", "));
537        IRNode body = node.getChild(0);
538        IRNode ret = node.getChild(1);
539        FunctionInfo functionInfo = node.getFunctionInfo();
540        return funcAttributes(functionInfo) + functionInfo.type.accept(this) + " " + functionInfo.name + "(" + args + ")" + "\n"
541                + PrintingUtils.align(node.getLevel() + 1) + "{\n"
542                + body.accept(this)
543                + (ret != null ? PrintingUtils.align(node.getLevel() + 2) + ret.accept(this) + "\n" : "")
544                + PrintingUtils.align(node.getLevel() + 1) + "}\n";
545    }
546
547    @Override
548    public String visit(FunctionDefinitionBlock node) {
549        StringBuilder code = new StringBuilder();
550        for (IRNode i : node.getChildren()) {
551            code.append("\n")
552                .append(PrintingUtils.align(node.getLevel()))
553                .append(i.accept(this))
554                .append(addComplexityInfo(i))
555                .append("\n");
556        }
557        return code.toString();
558    }
559
560    @Override
561    public String visit(FunctionRedefinition node) {
562        String args = node.getChildren().stream()
563                .skip(2)
564                .map(c -> c.accept(this))
565                .collect(Collectors.joining(", "));
566
567        IRNode body = node.getChild(0);
568        IRNode ret = node.getChild(1);
569        int level = node.getLevel();
570        FunctionInfo functionInfo = node.getFunctionInfo();
571        return funcAttributes(functionInfo) + functionInfo.type.accept(this) + " " + functionInfo.name + "(" + args + ")" + "\n"
572                + PrintingUtils.align(level + 1) + "{\n"
573                + body.accept(this)
574                + (ret != null ? PrintingUtils.align(level + 2) + ret.accept(this) + "\n" : "")
575                + PrintingUtils.align(level + 1) + "}";
576    }
577
578    @Override
579    public String visit(FunctionRedefinitionBlock node) {
580        StringBuilder code = new StringBuilder();
581        for (IRNode i : node.getChildren()) {
582            code.append("\n")
583                .append(PrintingUtils.align(node.getLevel()))
584                .append(i.accept(this))
585                .append(addComplexityInfo(i))
586                .append("\n");
587        }
588        return code.toString();
589    }
590
591    @Override
592    public String visit(If node) {
593        int level = node.getLevel();
594        String thenBlockString = PrintingUtils.align(level) + "{\n"
595                                 + node.getChild(If.IfPart.THEN.ordinal()).accept(this)
596                                 + PrintingUtils.align(level) + "}";
597
598        String elseBlockString = null;
599        if (node.getChild(If.IfPart.ELSE.ordinal()) != null) {
600            elseBlockString = PrintingUtils.align(level) + "{\n"
601                              + node.getChild(If.IfPart.ELSE.ordinal()).accept(this)
602                              + PrintingUtils.align(level) + "}";
603        }
604
605        return "if (" + node.getChild(If.IfPart.CONDITION.ordinal()).accept(this)+ ")\n"
606            + thenBlockString + (elseBlockString != null ? "\n"
607            + PrintingUtils.align(level) + "else\n" + elseBlockString : "");
608    }
609
610    @Override
611    public String visit(Initialization node) {
612        VariableInfo vi = node.getVariableInfo();
613        return attributes(vi) + vi.type.accept(this)+ " " + vi.name + " = "
614                + node.getChild(0).accept(this);
615    }
616
617    @Override
618    public String visit(Interface node) {
619        return "interface " + node.getName() + (node.getParentKlass() != null ? " extends "
620                + node.getParentKlass().getName() : "") + " {\n"
621                + (node.getChildren().size() > 0 ? node.getChild(0).accept(this) : "")
622                + "}\n";
623    }
624
625    @Override
626    public String visit(Klass node) {
627        TypeKlass thisKlass = node.getThisKlass();
628        String r = (ProductionParams.enableStrictFP.value() ? "strictfp " : "")
629                + (thisKlass.isFinal() ? "final " : "")
630                + (thisKlass.isAbstract() ? "abstract " : "")
631                + "class " + node.getName()
632                + (node.getParentKlass() != null && !node.getParentKlass().equals(TypeList.OBJECT)
633                ? " extends " + node.getParentKlass().getName() : "");
634        List<TypeKlass> interfaces = node.getInterfaces();
635        r += interfaces.stream()
636                .map(Type::getName)
637                .collect(Collectors.joining(", ", (interfaces.isEmpty() ? "" : " implements "), ""));
638        IRNode dataMembers = node.getChild(Klass.KlassPart.DATA_MEMBERS.ordinal());
639        IRNode constructors = node.getChild(Klass.KlassPart.CONSTRUCTORS.ordinal());
640        IRNode redefinedFunctions = node.getChild(Klass.KlassPart.REDEFINED_FUNCTIONS.ordinal());
641        IRNode overridenFunctions = node.getChild(Klass.KlassPart.OVERRIDEN_FUNCTIONS.ordinal());
642        IRNode memberFunctions = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS.ordinal());
643        IRNode memberFunctionDecls = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS_DECLARATIONS.ordinal());
644        IRNode printVariables = node.getChild(Klass.KlassPart.PRINT_VARIABLES.ordinal());
645        r += " {\n"
646             + (dataMembers != null ? (dataMembers.accept(this)+ "\n") : "")
647             + (constructors != null ? (constructors.accept(this)+ "\n") : "")
648             + (redefinedFunctions != null ? (redefinedFunctions.accept(this)+ "\n") : "")
649             + (overridenFunctions != null ? (overridenFunctions.accept(this)+ "\n") : "")
650             + (memberFunctionDecls != null ? (memberFunctionDecls.accept(this)+ "\n") : "")
651             + (memberFunctions != null ? (memberFunctions.accept(this)+ "\n") : "")
652             + printVariables.accept(this)
653             + "}\n";
654        return r;
655    }
656
657    @Override
658    public String visit(Literal node) {
659        Type resultType = node.getResultType();
660        Object value = node.getValue();
661        if (resultType.equals(TypeList.LONG)) {
662            return value.toString() + "L";
663        }
664        if (resultType.equals(TypeList.FLOAT)) {
665            return String.format((Locale) null,
666                "%EF",
667                Double.parseDouble(value.toString()));
668        }
669        if (resultType.equals(TypeList.DOUBLE)) {
670            return String.format((Locale) null,
671                "%E",
672                Double.parseDouble(value.toString()));
673        }
674        if (resultType.equals(TypeList.CHAR)) {
675            if ((Character) value == '\\') {
676                return "\'" + "\\\\" + "\'";
677            } else {
678                return "\'" + value.toString() + "\'";
679            }
680        }
681        if (resultType.equals(TypeList.SHORT)) {
682            return "(short) " + value.toString();
683        }
684        if (resultType.equals(TypeList.BYTE)) {
685            return "(byte) " + value.toString();
686        }
687        if (resultType.equals(TypeList.STRING)) {
688            // TOOD handle other non-printable
689            return "\"" + value.toString().replace("\n", "\\n") + "\"";
690        }
691        return value.toString();
692    }
693
694    @Override
695    public String visit(LocalVariable node) {
696        return node.getVariableInfo().name;
697    }
698
699    @Override
700    public String visit(LoopingCondition node) {
701        return node.getCondition().accept(this);
702    }
703
704    @Override
705    public String visit(MainKlass node) {
706        String name = node.getName();
707        IRNode dataMembers = node.getChild(MainKlass.MainKlassPart.DATA_MEMBERS.ordinal());
708        IRNode memberFunctions = node.getChild(MainKlass.MainKlassPart.MEMBER_FUNCTIONS.ordinal());
709        IRNode testFunction = node.getChild(MainKlass.MainKlassPart.TEST_FUNCTION.ordinal());
710        IRNode printVariables = node.getChild(MainKlass.MainKlassPart.PRINT_VARIABLES.ordinal());
711
712        return (ProductionParams.enableStrictFP.value() ? "strictfp " : "")
713                + "public class " + name + " {\n"
714                + dataMembers.accept(this)+ "\n"
715                + (memberFunctions != null ? memberFunctions.accept(this): "") + "\n"
716                + "    private void test()\n"
717                + "    {\n"
718                + testFunction.accept(this)
719                + "    }" + addComplexityInfo(testFunction) + "\n"
720                + printVariables.accept(this)
721                + "}\n\n";
722    }
723
724    @Override
725    public String visit(NonStaticMemberVariable node) {
726        IRNode object = node.getChild(0);
727        String objectString = object.accept(this);
728        VariableInfo value = node.getVariableInfo();
729        if (objectString.equals("this")) {
730            return value.name;
731        } else {
732            if (object instanceof VariableBase || object instanceof Function || object instanceof Literal) {
733                return objectString + "." + value.name;
734            } else {
735                return "(" + objectString + ")" + "." + value.name;
736            }
737        }
738    }
739
740    @Override
741    public String visit(Nothing node) {
742        return "";
743    }
744
745    @Override
746    public String visit(PrintVariables node) {
747        return FixedTrees.printVariablesAsFunction(node).accept(this);
748    }
749
750    @Override
751    public String visit(Return node) {
752        return "return " + node.getExpression().accept(this) + ";";
753    }
754
755    @Override
756    public String visit(Throw node) {
757        return "throw " + node.getThowable().accept(this) + ";";
758    }
759
760    @Override
761    public String visit(Statement node) {
762        return node.getChild(0).accept(this)+ (node.isSemicolonNeeded() ? ";" : "");
763    }
764
765    @Override
766    public String visit(StaticConstructorDefinition node) {
767        IRNode body = node.getChild(0);
768        return "static {\n"
769                + (body != null ? body.accept(this): "")
770                + PrintingUtils.align(node.getLevel()) + "}";
771    }
772
773    @Override
774    public String visit(StaticMemberVariable node) {
775        IRNode owner = node.getOwner();
776        VariableInfo info = node.getVariableInfo();
777        if (owner.equals(info.owner)) {
778            return info.name;
779        } else {
780            return info.owner.getName() + "." + info.name;
781        }
782    }
783
784    @Override
785    public String visit(Switch node) {
786        int level = node.getLevel();
787        int caseBlockIdx = node.getCaseBlockIndex();
788        String cases = "";
789        for (int i = 0; i < caseBlockIdx - 1; ++i) {
790            cases += PrintingUtils.align(level + 1);
791            if (node.getChild(i + 1) instanceof Nothing) {
792                cases += "default:\n";
793            } else {
794                cases += "case " + node.getChild(i + 1).accept(this)+ ":\n";
795            }
796
797            cases += node.getChild(i + caseBlockIdx).accept(this)+ "\n";
798        }
799        return "switch (" + node.getChild(0).accept(this)+ ")\n"
800               + PrintingUtils.align(level) + "{\n"
801               + cases
802               + PrintingUtils.align(level) + "}";
803    }
804
805    @Override
806    public String visit(TernaryOperator node) {
807        IRNode conditionalExp = node.getChild(TernaryOperator.TernaryPart.CONDITION.ordinal());
808        IRNode leftExp = node.getChild(TernaryOperator.TernaryPart.TRUE.ordinal());
809        IRNode rightExp = node.getChild(TernaryOperator.TernaryPart.FALSE.ordinal());
810        if (Objects.isNull(conditionalExp) || Objects.isNull(leftExp) || Objects.isNull(rightExp)) {
811            return "null";
812        }
813        return expressionToJavaCode(node, conditionalExp, Operator.Order.RIGHT) + " ? "
814                + expressionToJavaCode(node, leftExp, Operator.Order.RIGHT) + " : "
815                + expressionToJavaCode(node, rightExp, Operator.Order.RIGHT);
816    }
817
818    @Override
819    public String visit(Type node) {
820        return node.getName();
821    }
822
823    @Override
824    public String visit(TypeArray node) {
825        String r = node.getType().accept(this);
826        for (int i = 0; i < node.getDimensions(); i++) {
827            r += "[]";
828        }
829        return r;
830    }
831
832    @Override
833    public String visit(UnaryOperator node) {
834        IRNode exp = node.getChild(0);
835        if (node.isPrefix()) {
836            return operatorToJaveCode(node.getOperationKind())
837                    + (exp instanceof Operator ? " " : "")
838                    + expressionToJavaCode(node, exp, Operator.Order.LEFT);
839        } else {
840            return expressionToJavaCode(node, exp, Operator.Order.RIGHT)
841                    + (exp instanceof Operator ? " " : "")
842                    + operatorToJaveCode(node.getOperationKind());
843        }
844    }
845
846    @Override
847    public String visit(VariableDeclaration node) {
848        VariableInfo vi = node.getVariableInfo();
849        return attributes(vi) + vi.type.accept(this)+ " " + vi.name;
850    }
851
852    @Override
853    public String visit(VariableDeclarationBlock node) {
854        StringBuilder code = new StringBuilder();
855        for (IRNode i : node.getChildren()) {
856            code.append(PrintingUtils.align(node.getLevel()))
857                .append(i.accept(this))
858                .append(addComplexityInfo(i))
859                .append("\n");
860        }
861        return code.toString();
862    }
863
864    @Override
865    public String visit(While node) {
866        IRNode header = node.getChild(While.WhilePart.HEADER.ordinal());
867        IRNode body1 = node.getChild(While.WhilePart.BODY1.ordinal());
868        IRNode body2 = node.getChild(While.WhilePart.BODY2.ordinal());
869        IRNode body3 = node.getChild(While.WhilePart.BODY3.ordinal());
870        int level = node.getLevel();
871        Loop loop = node.getLoop();
872        return loop.initialization.accept(this)+ "\n"
873                + header.accept(this)
874                + PrintingUtils.align(level) + "while (" + loop.condition.accept(this)+ ")\n"
875                + PrintingUtils.align(level) + "{\n"
876                + body1.accept(this)
877                + PrintingUtils.align(level + 1) + loop.manipulator.accept(this)+ ";\n"
878                + body2.accept(this)
879                + body3.accept(this)
880                + PrintingUtils.align(level) + "}";
881    }
882
883    @Override
884    public String visit(CatchBlock node) {
885        StringBuilder result = new StringBuilder();
886        int level = node.getLevel();
887        result.append(PrintingUtils.align(level)).append("catch(");
888        result.append(node.throwables.get(0).accept(this));
889        for (int i = 1; i < node.throwables.size(); i++) {
890            result.append(" | ").append(node.throwables.get(i).accept(this));
891        }
892        result.append(" ex) {\n");
893        result.append(node.getChild(0).accept(this));
894        result.append(PrintingUtils.align(level)).append("}\n");
895        return result.toString();
896    }
897
898    @Override
899    public String visit(TryCatchBlock node) {
900        StringBuilder result = new StringBuilder();
901        List<? extends IRNode> childs = node.getChildren();
902        IRNode body = childs.get(0);
903        IRNode finallyBody = childs.get(1);
904        int level = node.getLevel();
905        result.append("try {\n")
906                .append(body.accept(this)).append("\n")
907                .append(PrintingUtils.align(level))
908                .append("}\n");
909        for (int i = 2; i < childs.size(); i++) {
910            result.append(childs.get(i).accept(this));
911        }
912        if (finallyBody != null) {
913            String finallyContent = finallyBody.accept(this);
914            if (!finallyContent.isEmpty()) {
915                result.append(PrintingUtils.align(level)).append("finally {\n")
916                        .append(finallyContent).append("\n")
917                        .append(PrintingUtils.align(level)).append("}\n");
918            }
919        }
920        return result.toString();
921    }
922}
923