1 /*
  2  * Copyright (c) 2024, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.code.tools.renderer;
 27 
 28 import java.lang.reflect.code.Block;
 29 import java.lang.reflect.code.Body;
 30 import java.lang.reflect.code.op.CoreOp;
 31 import java.lang.reflect.code.Op;
 32 import java.lang.reflect.code.Value;
 33 
 34 import java.io.*;
 35 import java.lang.reflect.code.op.ExternalizableOp;
 36 import java.lang.reflect.code.type.JavaType;
 37 import java.nio.charset.StandardCharsets;
 38 
 39 /**
 40  * Created by gfrost
 41  */
 42 public final class SRRenderer extends CommonRenderer<SRRenderer> {
 43 
 44     static class AttributeMapper {
 45         static String toString(Object value) {
 46             if (value instanceof Integer i && i >= 0) {
 47                 return Integer.toString(i);
 48             } else if (value == null) {
 49                 return "null";
 50             } else {
 51                 return "\"" + quote(value.toString()) + "\"";
 52             }
 53         }
 54     }
 55 
 56     // Copied from com.sun.tools.javac.util.Convert
 57     static String quote(String s) {
 58         StringBuilder buf = new StringBuilder();
 59         for (int i = 0; i < s.length(); i++) {
 60             buf.append(quote(s.charAt(i)));
 61         }
 62         return buf.toString();
 63     }
 64 
 65     /**
 66      * Escapes a character if it has an escape sequence or is
 67      * non-printable ASCII.  Leaves non-ASCII characters alone.
 68      */
 69     static String quote(char ch) {
 70         switch (ch) {
 71             case '\b':  return "\\b";
 72             case '\f':  return "\\f";
 73             case '\n':  return "\\n";
 74             case '\r':  return "\\r";
 75             case '\t':  return "\\t";
 76             case '\'':  return "\\'";
 77             case '\"':  return "\\\"";
 78             case '\\':  return "\\\\";
 79             default:
 80                 return (isPrintableAscii(ch))
 81                         ? String.valueOf(ch)
 82                         : String.format("\\u%04x", (int) ch);
 83         }
 84     }
 85 
 86     /**
 87      * Is a character printable ASCII?
 88      */
 89     static boolean isPrintableAscii(char ch) {
 90         return ch >= ' ' && ch <= '~';
 91     }
 92 
 93     SRRenderer() {
 94         super();
 95     }
 96 
 97     SRRenderer caretLabelTarget(String name) {
 98         return caret().labelTarget(name).self();
 99     }
100 
101     SRRenderer atIdentifier(String name) {
102         return at().identifier(name).self();
103     }
104 
105     SRRenderer percentLiteral(String name) {
106         return percent().literal(name).self();
107     }
108 
109     SRRenderer spaceColonSpace() {
110         return space().colon().space();
111     }
112 
113     SRRenderer spaceEqualSpace() {
114         return space().equal().space();
115     }
116 
117     public void write(Op op) {
118         GlobalValueBlockNaming gn = new GlobalValueBlockNaming();
119         write(gn, op);
120         nl();
121     }
122 
123     public void write(GlobalValueBlockNaming gn, Block.Reference successor) {
124         caretLabelTarget(gn.getBlockName(successor.targetBlock()));
125         if (!successor.arguments().isEmpty()) {
126             oparen().commaSeparatedList();
127             for (var a : successor.arguments()) {
128                 commaSeparator();
129                 percentLiteral(gn.getValueName(a));
130             }
131             cparen();
132         }
133 
134     }
135 
136     public void write(GlobalValueBlockNaming gn, Op op) {
137         keyword(op.opName());
138         if (!op.operands().isEmpty()) {
139             space().spaceSeparatedList();
140             for (var v : op.operands()) {
141                 spaceSeparator();
142                 percentLiteral(gn.getValueName(v));
143             }
144         }
145         if (!op.successors().isEmpty()) {
146             space().spaceSeparatedList();
147             for (Block.Reference sb : op.successors()) {
148                 spaceSeparator();
149                 write(gn, sb);
150             }
151         }
152 
153         if (op instanceof ExternalizableOp exop) {
154             if (!exop.attributes().isEmpty()) {
155                 space().spaceSeparatedList();
156                 for (var e : exop.attributes().entrySet()) {
157                     spaceSeparator();
158                     String name = e.getKey();
159                     if (!name.isEmpty()) {
160                         atIdentifier(name).equal().identifier(AttributeMapper.toString(e.getValue()));
161                     } else {
162                         atIdentifier(AttributeMapper.toString(e.getValue()));
163                     }
164                 }
165             }
166         }
167 
168         if (!op.bodies().isEmpty()) {
169             int nBodies = op.bodies().size();
170             if (nBodies == 1) {
171                 space();
172             } else {
173                 nl().in().in();
174             }
175             // @@@ separated list state does not nest as state.first gets overwritten
176             boolean first = true;
177             for (Body body : op.bodies()) {
178                 if (!first) {
179                     nl();
180                 }
181                 write(gn, body);
182                 first = false;
183             }
184             if (nBodies > 1) {
185                 out().out();
186             }
187         }
188 
189         semicolon();
190     }
191 
192     public void write(GlobalValueBlockNaming gn, Block block, boolean isEntryBlock) {
193         if (!isEntryBlock) {
194             caretLabelTarget(gn.getBlockName(block));
195             if (!block.parameters().isEmpty()) {
196                 oparen().commaSpaceSeparatedList();
197                 for (var v : block.parameters()) {
198                     commaSpaceSeparator();
199                     writeValueDecl(gn, v);
200                 }
201                 cparen();
202             }
203             colon().nl();
204         }
205         in();
206         for (Op op : block.ops()) {
207             Op.Result or = op.result();
208             if (!or.type().equals(JavaType.VOID)) {
209                 writeValueDecl(gn, or);
210                 spaceEqualSpace();
211             }
212             write(gn, op);
213             nl();
214         }
215         out();
216     }
217 
218     public void write(GlobalValueBlockNaming gn, Body body) {
219         Block eb = body.entryBlock();
220         oparen().commaSpaceSeparatedList();
221         for (var v : eb.parameters()) {
222             commaSpaceSeparator();
223             writeValueDecl(gn, v);
224         }
225         cparen().type(body.bodyType().returnType().toString()).space().rarrow().space().obrace().nl();
226         in();
227         boolean isEntryBlock = true;
228         for (Block b : body.blocks()) {
229             if (!isEntryBlock) {
230                 nl();
231             }
232             write(gn, b, isEntryBlock);
233             isEntryBlock = false;
234         }
235         out();
236         cbrace();
237     }
238 
239     public void writeValueDecl(GlobalValueBlockNaming gn, Value v) {
240         percentLiteral(gn.getValueName(v)).spaceColonSpace().type(v.type().toString());
241     }
242 
243     // @@@ Not used
244     public void write(GlobalValueBlockNaming gn, CoreOp.FuncOp fRep) {
245         this.append(fRep.opName());// w.write(name);
246         if (!fRep.operands().isEmpty()) {
247             space().spaceSeparatedList();
248             for (var v : fRep.operands()) {
249                 spaceSeparator();
250                 percentLiteral(gn.getValueName(v));
251             }
252         }
253         if (!fRep.successors().isEmpty()) {
254             space().spaceSeparatedList();
255             for (Block.Reference sb : fRep.successors()) {
256                 spaceSeparator();
257                 write(gn, sb);
258             }
259         }
260         if (!fRep.attributes().isEmpty()) {
261             space();
262             for (var e : fRep.attributes().entrySet()) {
263                 String name = e.getKey();
264                 String value = AttributeMapper.toString(e.getValue());
265                 op("@");
266                 if (name.isEmpty()) {
267                     literal(value);
268                 } else {
269                     identifier(name).equal().literal(value);
270                 }
271             }
272         }
273         if (!fRep.bodies().isEmpty()) {
274             space().newlineSeparatedList();
275             for (Body body : fRep.bodies()) {
276                 newlineSeparator();
277                 write(gn, body);
278             }
279         }
280 
281     }
282 
283     public static void write(Writer writer, Op op) {
284         new SRRenderer().writer(writer).write(op);
285     }
286 
287     public static void write(OutputStream out, Op op) {
288         write(new OutputStreamWriter(out, StandardCharsets.UTF_8), op);
289     }
290 
291     public static String stringify(Op op) {
292         StringWriter sw = new StringWriter();
293         write(sw, op);
294         return sw.toString();
295     }
296 
297     public static String colorize(TextRenderer.TokenColorMap tokenColorMap, Op op) {
298         StringWriter sw = new StringWriter();
299         new SRRenderer().writer(sw).colorize(tokenColorMap).write(op);
300         return sw.toString();
301     }
302 
303     public static String colorize(Op op) {
304         return colorize(new TextRenderer.TokenColorMap(), op);
305     }
306 }