1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package vm.mlvm.meth.share.transform.v2;
25 
26 import java.io.*;
27 import java.util.*;
28 
29 import vm.mlvm.share.Env;
30 
31 import nsk.share.test.TestUtils;
32 
33 
34 public class MHMacroTF extends MHTF {
35 
36     private final String name;
37     private final Collection<MHCall> calls = new HashSet<>();
38     private final Collection<MHCall> outboundCalls = new LinkedHashSet<>();
39     private MHCall inboundCall = null;
40     private final Collection<MHTF> tfs = new LinkedHashSet<>();
41 
MHMacroTF(String name)42     public MHMacroTF(String name) {
43         this.name = name;
44     }
45 
addNewInboundCall(MHCall call)46     private void addNewInboundCall(MHCall call) {
47         TestUtils.assertNotInCollection(this.calls, call);
48 
49         this.inboundCall = call;
50         this.calls.add(call);
51     }
52 
addOutboundCall(MHCall call)53     public void addOutboundCall(MHCall call) {
54         TestUtils.assertNotInCollection(this.calls, call);
55 
56         this.calls.add(call);
57         this.outboundCalls.add(call);
58     }
59 
addTransformation(MHTF tf)60     public MHCall addTransformation(MHTF tf) throws IllegalArgumentException,
61             NoSuchMethodException, IllegalAccessException {
62         TestUtils.assertNotInCollection(this.tfs, tf);
63         for (MHCall c : tf.getOutboundCalls()) {
64             TestUtils.assertInCollection(this.calls, c);
65         }
66 
67         Env.traceDebug("MHMacroTF: adding %s", tf);
68 
69         this.tfs.add(tf);
70         MHCall inboundCall = tf.computeInboundCall();
71         addNewInboundCall(inboundCall);
72 
73         Env.traceDebug("MHMacroTF: current inbound call: %s", inboundCall);
74 
75         return inboundCall;
76     }
77 
78     @Override
computeInboundCall()79     public MHCall computeInboundCall() {
80         return inboundCall;
81     }
82 
83     @Override
getOutboundCalls()84     public MHCall[] getOutboundCalls() {
85         return this.outboundCalls.toArray(new MHCall[0]);
86     }
87 
88     @Override
getSubTFs()89     public MHTF[] getSubTFs() {
90         return this.tfs.toArray(new MHTF[0]);
91     }
92 
93     @Override
getName()94     protected String getName() {
95         return name + " graph";
96     }
97 
98     @Override
getDescription()99     protected String getDescription() {
100         StringBuilder result = new StringBuilder("\n");
101         Deque<PrettyPrintElement> printElements = new ArrayDeque<>();
102         printElements.add(new PrettyPrintElement("", "  ", inboundCall, true));
103         PrettyPrintElement current;
104         while (!printElements.isEmpty()) {
105             current = printElements.pop();
106             appendElement(result, printElements, current.topCallPrefix,
107                     current.subCallPrefix, current.topCall,
108                     current.isRecursive);
109         }
110         String filename = getName() + "-"
111                 + Long.toString(System.currentTimeMillis(), Character.MAX_RADIX)
112                 + ".txt";
113         try (Writer writer = new FileWriter(filename)) {
114             writer.write(result.toString());
115         } catch (IOException e) {
116             return result.toString();
117         }
118         return "see " + filename;
119     }
120 
appendElement(StringBuilder result, Deque<PrettyPrintElement> deque, String topCallPrefix, String subCallPrefix, MHCall topCall, boolean isRecursive)121     private void appendElement(StringBuilder result,
122             Deque<PrettyPrintElement> deque, String topCallPrefix,
123             String subCallPrefix, MHCall topCall, boolean isRecursive) {
124         MHCall[] outCalls = topCall.getTarget().getOutboundCalls();
125         boolean printSubTree = (isRecursive && outCalls.length > 0);
126         result.append(topCall.prettyPrint(topCallPrefix + "->",
127                 subCallPrefix + (printSubTree ? "|" : " ") + " ") + "\n");
128         if (printSubTree) {
129             for (int n = outCalls.length - 1, i = n; i >= 0; --i) {
130                 MHCall outCall = outCalls[i];
131                 boolean isLastSubtree = (i == n);
132                 String curTopCallPrefix = subCallPrefix
133                         + (isLastSubtree ? "\\" : "|")
134                         + "--";
135                 String curSubCallPrefix = subCallPrefix
136                         + (isLastSubtree ? " " : "|")
137                         + "  ";
138                 deque.addFirst(new PrettyPrintElement(curTopCallPrefix,
139                         curSubCallPrefix, outCall,
140                         !outboundCalls.contains(outCall)));
141             }
142         }
143     }
144 
145 
146     private static class PrettyPrintElement {
147         final String topCallPrefix;
148         final String subCallPrefix;
149         final MHCall topCall;
150         final boolean isRecursive;
151 
PrettyPrintElement(String topCallPrefix, String subCallPrefix, MHCall topCall, boolean isRecursive)152         private PrettyPrintElement(String topCallPrefix,
153                 String subCallPrefix, MHCall topCall, boolean isRecursive) {
154             this.topCallPrefix = topCallPrefix;
155             this.subCallPrefix = subCallPrefix;
156             this.topCall = topCall;
157             this.isRecursive = isRecursive;
158         }
159     }
160 
161 }
162