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 hat.tools.text;
27
28 import java.io.*;
29
30 import jdk.incubator.code.*;
31 import jdk.incubator.code.dialect.java.JavaType;
32 import jdk.incubator.code.dialect.java.WildcardType;
33 import jdk.incubator.code.extern.ExternalizedOp;
34 import jdk.incubator.code.extern.ExternalizedTypeElement;
35
36 import java.lang.reflect.Array;
37 import java.nio.charset.StandardCharsets;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.function.Consumer;
42 import java.util.function.Function;
43 import java.util.stream.Collectors;
44 import java.util.stream.IntStream;
45
46 /**
47 * A writer of code models to the textual form.
48 * <p>
49 * A code model in textual form may be parsed back into the runtime form by parsing it.
50 */
51 public final class OpCodeBuilder {
52
53 // Hacked from jdk.incubator.code/share/classes/jdk/incubator/code/dialect/java/impl/JavaTypeUtils.java
54 static class JavaTypeUtils{
55
56 // useful type identifiers
57
58 /** Inflated Java class type name */
59 public static final String JAVA_TYPE_CLASS_NAME = "java.type.class";
60 /** Inflated Java array type name */
61 public static final String JAVA_TYPE_ARRAY_NAME = "java.type.array";
62 /** Inflated Java wildcard type name */
63 public static final String JAVA_TYPE_WILDCARD_NAME = "java.type.wildcard";
64 /** Inflated Java type var name */
65 public static final String JAVA_TYPE_VAR_NAME = "java.type.var";
66 /** Inflated Java primitive type name */
67 public static final String JAVA_TYPE_PRIMITIVE_NAME = "java.type.primitive";
68
69 /** Inflated Java field reference name */
70 public static final String JAVA_REF_FIELD_NAME = "java.ref.field";
71 /** Inflated Java method reference name */
72 public static final String JAVA_REF_METHOD_NAME = "java.ref.method";
73 /** Inflated Java constructor reference name */
74 public static final String JAVA_REF_CONSTRUCTOR_NAME = "java.ref.constructor";
75 /** Inflated Java record name */
76 public static final String JAVA_REF_RECORD_NAME = "java.ref.record";
77
78 /** Flattened Java type name */
79 public static final String JAVA_TYPE_FLAT_NAME_PREFIX = "java.type:";
80 /** Flattened Java reference name */
81 public static final String JAVA_REF_FLAT_NAME_PREFIX = "java.ref:";
82
83 /**
84 * An enum modelling the Java type form kind. Useful for switching.
85 */
86 public enum Kind {
87 /** A flattened type form */
88 FLATTENED_TYPE,
89 /** A flattened reference form */
90 FLATTENED_REF,
91 /** An inflated type form */
92 INFLATED_TYPE,
93 /** An inflated reference form */
94 INFLATED_REF,
95 /** Some other form */
96 OTHER;
97
98 /**
99 * Constructs a new kind from an externalized type form
100 * @param tree the externalized type form
101 * @return the kind modelling {@code tree}
102 */
103 public static Kind of(ExternalizedTypeElement tree) {
104 return switch (tree.identifier()) {
105 case JAVA_TYPE_CLASS_NAME, JAVA_TYPE_ARRAY_NAME,
106 JAVA_TYPE_PRIMITIVE_NAME, JAVA_TYPE_WILDCARD_NAME,
107 JAVA_TYPE_VAR_NAME -> INFLATED_TYPE;
108 case JAVA_REF_FIELD_NAME, JAVA_REF_CONSTRUCTOR_NAME,
109 JAVA_REF_METHOD_NAME, JAVA_REF_RECORD_NAME -> INFLATED_REF;
110 case String s when s.startsWith(JAVA_TYPE_FLAT_NAME_PREFIX) -> FLATTENED_TYPE;
111 case String s when s.startsWith(JAVA_REF_FLAT_NAME_PREFIX) -> FLATTENED_REF;
112 default -> OTHER;
113 };
114 }
115 }
116 private static ExternalizedTypeElement nameToType(String name) {
117 return ExternalizedTypeElement.of(name);
118 }
119 private static <T> T select(ExternalizedTypeElement tree, int index, Function<ExternalizedTypeElement, T> valueFunc) {
120 if (index >= tree.arguments().size()) {
121 throw new UnsupportedOperationException();
122 }
123 return valueFunc.apply(tree.arguments().get(index));
124 }
125 private static <T> List<T> selectFrom(ExternalizedTypeElement tree, int startIncl, Function<ExternalizedTypeElement, T> valueFunc) {
126 if (startIncl >= tree.arguments().size()) {
127 return List.of();
128 }
129 return IntStream.range(startIncl, tree.arguments().size())
130 .mapToObj(i -> valueFunc.apply(tree.arguments().get(i)))
131 .toList();
132 }
133
134 private static String typeToName(ExternalizedTypeElement tree) {
135 if (!tree.arguments().isEmpty()) {
136 throw new UnsupportedOperationException();
137 }
138 return tree.identifier();
139 }
140
141 /**
142 * {@return a flat string modelling the provided inflated Java reference form}.
143 * @param tree the inflated Java type form
144 */
145 public static String toExternalRefString(ExternalizedTypeElement tree) {
146 return switch (tree.identifier()) {
147 case JAVA_REF_FIELD_NAME -> {
148 String owner = select(tree, 0, JavaTypeUtils::toExternalTypeString);
149 String fieldName = select(tree, 1, JavaTypeUtils::typeToName);
150 String fieldType = select(tree, 2, JavaTypeUtils::toExternalTypeString);
151 yield String.format("%s::%s:%s", owner, fieldName, fieldType);
152 }
153 case JAVA_REF_METHOD_NAME -> {
154 String owner = select(tree, 0, JavaTypeUtils::toExternalTypeString);
155 ExternalizedTypeElement nameAndArgs = select(tree, 1, Function.identity());
156 String methodName = nameAndArgs.identifier();
157 List<String> paramTypes = selectFrom(nameAndArgs, 0, JavaTypeUtils::toExternalTypeString);
158 String restype = select(tree, 2, JavaTypeUtils::toExternalTypeString);
159 yield String.format("%s::%s(%s):%s", owner, methodName, String.join(", ", paramTypes), restype);
160 }
161 case JAVA_REF_CONSTRUCTOR_NAME -> {
162 String owner = select(tree, 0, JavaTypeUtils::toExternalTypeString);
163 ExternalizedTypeElement nameAndArgs = select(tree, 1, Function.identity());
164 List<String> paramTypes = selectFrom(nameAndArgs, 0, JavaTypeUtils::toExternalTypeString);
165 yield String.format("%s::(%s)", owner, String.join(", ", paramTypes));
166 }
167 case JAVA_REF_RECORD_NAME -> {
168 String owner = select(tree, 0, JavaTypeUtils::toExternalTypeString);
169 List<String> components = selectFrom(tree, 1, Function.identity()).stream()
170 .map(t -> {
171 String componentName = t.identifier();
172 String componentType = select(t, 0, JavaTypeUtils::toExternalTypeString);
173 return String.format("%s %s", componentType, componentName);
174 }).toList();
175 yield String.format("(%s)%s", String.join(", ", components), owner);
176 }
177 default -> throw new UnsupportedOperationException();
178 };
179 }
180
181 private static boolean isSameType(ExternalizedTypeElement tree, TypeElement typeElement) {
182 return tree.equals(typeElement.externalize());
183 }
184
185 /**
186 * {@return a flat string modelling the provided inflated Java type form}.
187 * @param tree the inflated Java type form
188 */
189 public static String toExternalTypeString(ExternalizedTypeElement tree) {
190 return switch (tree.identifier()) {
191 case JAVA_TYPE_CLASS_NAME -> {
192 String className = select(tree, 0, JavaTypeUtils::typeToName);
193 ExternalizedTypeElement enclosing = select(tree, 1, Function.identity());
194 String typeargs = tree.arguments().size() == 2 ?
195 "" :
196 selectFrom(tree, 2, JavaTypeUtils::toExternalTypeString).stream()
197 .collect(Collectors.joining(", ", "<", ">"));
198 if (isSameType(enclosing, JavaType.VOID)) {
199 yield String.format("%s%s", className, typeargs);
200 } else {
201 String enclosingString = toExternalTypeString(enclosing);
202 yield String.format("%s::%s%s", enclosingString, className, typeargs);
203 }
204 }
205 case JAVA_TYPE_ARRAY_NAME -> {
206 String componentType = select(tree, 0, JavaTypeUtils::toExternalTypeString);
207 yield String.format("%s[]", componentType);
208 }
209 case JAVA_TYPE_WILDCARD_NAME -> {
210 WildcardType.BoundKind boundKind = select(tree, 0, t -> WildcardType.BoundKind.valueOf(typeToName(t)));
211 ExternalizedTypeElement bound = select(tree, 1, Function.identity());
212 yield boundKind == WildcardType.BoundKind.EXTENDS && isSameType(bound, JavaType.J_L_OBJECT) ?
213 "?" :
214 String.format("? %s %s", boundKind.name().toLowerCase(), toExternalTypeString(bound));
215 }
216 case JAVA_TYPE_VAR_NAME -> {
217 String tvarName = select(tree, 0, JavaTypeUtils::typeToName);
218 String owner = select(tree, 1, t ->
219 switch (Kind.of(t)) {
220 case INFLATED_REF -> "&" + toExternalRefString(t);
221 case INFLATED_TYPE -> toExternalTypeString(t);
222 default -> throw new UnsupportedOperationException();
223 });
224 ExternalizedTypeElement bound = select(tree, 2, Function.identity());
225 yield isSameType(bound, JavaType.J_L_OBJECT) ?
226 String.format("%s::<%s>", owner, tvarName) :
227 String.format("%s::<%s extends %s>", owner, tvarName, toExternalTypeString(bound));
228 }
229 case JAVA_TYPE_PRIMITIVE_NAME -> select(tree, 0, JavaTypeUtils::typeToName);
230 default -> throw new UnsupportedOperationException();
231 };
232 }
233
234 /**
235 * {@return the flat Java form corresponding to the provided inflated Java form}
236 * @param tree the inflated Java form
237 */
238 public static ExternalizedTypeElement flatten(ExternalizedTypeElement tree) {
239 return switch (Kind.of(tree)) {
240 case INFLATED_TYPE -> nameToType(String.format("%s\"%s\"", JAVA_TYPE_FLAT_NAME_PREFIX, toExternalTypeString(tree)));
241 case INFLATED_REF -> nameToType(String.format("%s\"%s\"", JAVA_REF_FLAT_NAME_PREFIX, toExternalRefString(tree)));
242 default -> ExternalizedTypeElement.of(tree.identifier(), tree.arguments().stream().map(JavaTypeUtils::flatten).toList());
243 };
244 }
245
246 }
247 /**
248 * The attribute name associated with the location attribute.
249 */
250 static final String ATTRIBUTE_LOCATION = "loc";
251
252 static final class GlobalValueBlockNaming implements Function<CodeItem, String> {
253 final Map<CodeItem, String> gn;
254 int valueOrdinal = 0;
255
256 GlobalValueBlockNaming() {
257 this.gn = new HashMap<>();
258 }
259
260 private String name(Block b) {
261 Block p = b.ancestorBlock();
262 return (p == null ? "block_" : name(p) + "_") + b.index();
263 }
264
265 @Override
266 public String apply(CodeItem codeItem) {
267 return switch (codeItem) {
268 case Block block -> gn.computeIfAbsent(block, _b -> name(block));
269 case Value value -> gn.computeIfAbsent(value, _v -> String.valueOf(valueOrdinal++));
270 default -> throw new IllegalStateException("Unexpected code item: " + codeItem);
271 };
272 }
273 }
274
275 static final class AttributeMapper {
276 static String toString(Object value) {
277 if (value == ExternalizedOp.NULL_ATTRIBUTE_VALUE) {
278 return "null";
279 }
280
281 StringBuilder sb = new StringBuilder();
282 toString(value, sb);
283 return sb.toString();
284 }
285
286 static void toString(Object o, StringBuilder sb) {
287 if (o.getClass().isArray()) {
288 // note, while we can't parse back the array representation, this might be useful
289 // for non-externalizable ops that want better string representation of array attribute values (e.g. ONNX)
290 arrayToString(o, sb);
291 } else {
292 switch (o) {
293 case Integer i -> sb.append(i);
294 case Long l -> sb.append(l).append('L');
295 case Float f -> sb.append(f).append('f');
296 case Double d -> sb.append(d).append('d');
297 case Character c -> sb.append('\'').append(c).append('\'');
298 case Boolean b -> sb.append(b);
299 case TypeElement te -> sb.append(JavaTypeUtils.flatten(te.externalize()).toString());
300 default -> { // fallback to a string
301 sb.append('"');
302 quote(o.toString(), sb);
303 sb.append('"');
304 }
305 }
306 }
307 }
308
309 static void arrayToString(Object a, StringBuilder sb) {
310 boolean first = true;
311 sb.append("[");
312 for (int i = 0; i < Array.getLength(a); i++) {
313 if (!first) {
314 sb.append(", ");
315 }
316 toString(Array.get(a, i), sb);
317 first = false;
318 }
319 sb.append("]");
320 }
321 }
322
323 static void quote(String s, StringBuilder sb) {
324 for (int i = 0; i < s.length(); i++) {
325 sb.append(quote(s.charAt(i)));
326 }
327 }
328
329 /**
330 * Escapes a character if it has an escape sequence or is
331 * non-printable ASCII. Leaves non-ASCII characters alone.
332 */
333 // Copied from com.sun.tools.javac.util.Convert
334 static String quote(char ch) {
335 return switch (ch) {
336 case '\b' -> "\\b";
337 case '\f' -> "\\f";
338 case '\n' -> "\\n";
339 case '\r' -> "\\r";
340 case '\t' -> "\\t";
341 case '\'' -> "\\'";
342 case '\"' -> "\\\"";
343 case '\\' -> "\\\\";
344 default -> (isPrintableAscii(ch))
345 ? String.valueOf(ch)
346 : String.format("\\u%04x", (int) ch);
347 };
348 }
349
350 /**
351 * Is a character printable ASCII?
352 */
353 static boolean isPrintableAscii(char ch) {
354 return ch >= ' ' && ch <= '~';
355 }
356
357 static final class IndentWriter {
358 static final int INDENT = 2;
359 private final Writer w;
360 private int indent;
361 private boolean writeIndent = true;
362
363 IndentWriter(Writer w) {
364 this(w, 0);
365 }
366
367 IndentWriter(Writer w, int indent) {
368 this.w = w;
369 this.indent = indent;
370 }
371
372
373 public void write(String s) {
374 try {
375 if (writeIndent) {
376 w.write(" ".repeat(indent));
377 writeIndent = false;
378 }
379 w.write(s);
380 } catch (IOException e) {
381 throw new UncheckedIOException(e);
382 }
383
384 }
385
386 public void nl(){
387 write("\n");
388 writeIndent=true;
389 }
390
391 public void symbol(String symbol){
392 write(symbol);
393 }
394
395 void space(){
396 write(" ");
397 }
398
399 void in() {
400 indent += INDENT;
401 }
402 void out() {
403 indent -= INDENT;
404 }
405 }
406
407 /**
408 * Computes global names for blocks and values in a code model.
409 * <p>
410 * The code model is traversed in the same order as if the model
411 * was written. Therefore, the names in the returned map will the
412 * same as the names that are written. This can be useful for debugging
413 * and testing.
414 *
415 * @param root the code model
416 * @return the map of computed names, modifiable
417 */
418 public static Function<CodeItem, String> computeGlobalNames(Op root) {
419 OpCodeBuilder w = new OpCodeBuilder(Writer.nullWriter());
420 w.writeOp(root);
421 return w.namer();
422 }
423
424 /**
425 * Writes a code model (an operation) to the output stream, using the UTF-8 character set.
426 *
427 * @param out the output stream
428 * @param op the code model
429 */
430 public static void writeTo(OutputStream out, Op op, Option... options) {
431 writeTo(new OutputStreamWriter(out, StandardCharsets.UTF_8), op, options);
432 }
433
434 /**
435 * Writes a code model (an operation) to the character stream.
436 * <p>
437 * The character stream will be flushed after the model is writen.
438 *
439 * @param w the character stream
440 * @param op the code model
441 * @param options the writer options
442 */
443 public static void writeTo(Writer w, Op op, Option... options) {
444 OpCodeBuilder ow = new OpCodeBuilder(w, options);
445 ow.writeOp(op);
446 try {
447 // @@@ Is this needed?
448 w.flush();
449 } catch (IOException e) {
450 throw new UncheckedIOException(e);
451 }
452 }
453
454 /**
455 * Writes a code model (an operation) to a string.
456 *
457 * @param op the code model
458 * @param options the writer options
459 */
460 public static String toText(Op op, OpCodeBuilder.Option... options) {
461 StringWriter w = new StringWriter();
462 writeTo(w, op, options);
463 return w.toString();
464 }
465
466 /**
467 * An option that affects the writing operations.
468 */
469 public sealed interface Option {
470 }
471
472 /**
473 * An option describing the function to use for naming code items.
474 */
475 public sealed interface CodeItemNamerOption extends Option
476 permits NamerOptionImpl {
477
478 static CodeItemNamerOption of(Function<CodeItem, String> named) {
479 return new NamerOptionImpl(named);
480 }
481
482 static CodeItemNamerOption defaultValue() {
483 return of(new GlobalValueBlockNaming());
484 }
485
486 Function<CodeItem, String> namer();
487 }
488 private record NamerOptionImpl(Function<CodeItem, String> namer) implements CodeItemNamerOption {
489 }
490
491 /**
492 * An option describing whether location information should be written or dropped.
493 */
494 public enum LocationOption implements Option {
495 /** Writes location */
496 WRITE_LOCATION,
497 /** Drops location */
498 DROP_LOCATION;
499
500 public static LocationOption defaultValue() {
501 return WRITE_LOCATION;
502 }
503 }
504
505 /**
506 * An option describing whether an operation's descendant code elements should be written or dropped.
507 */
508 public enum OpDescendantsOption implements Option {
509 /** Writes descendants of an operation, if any */
510 WRITE_DESCENDANTS,
511 /** Drops descendants of an operation, if any */
512 DROP_DESCENDANTS;
513
514 public static OpDescendantsOption defaultValue() {
515 return WRITE_DESCENDANTS;
516 }
517 }
518
519 /**
520 * An option describing whether an operation's result be written or dropped if its type is void.
521 */
522 public enum VoidOpResultOption implements Option {
523 /** Writes void operation result */
524 WRITE_VOID,
525 /** Drops void operation result */
526 DROP_VOID;
527
528 public static VoidOpResultOption defaultValue() {
529 return DROP_VOID;
530 }
531 }
532
533 final Function<CodeItem, String> namer;
534 final IndentWriter w;
535 final boolean dropLocation;
536 final boolean dropOpDescendants;
537 final boolean writeVoidOpResult;
538
539 /**
540 * Creates a writer of code models (operations) to their textual form.
541 *
542 * @param w the character stream writer to write the textual form.
543 */
544 public OpCodeBuilder(Writer w) {
545 this.w = new IndentWriter(w);
546 this.namer = new GlobalValueBlockNaming();
547 this.dropLocation = false;
548 this.dropOpDescendants = false;
549 this.writeVoidOpResult = false;
550 }
551
552 /**
553 * Creates a writer of code models (operations) to their textual form.
554 *
555 * @param w the character stream writer to write the textual form.
556 * @param options the writer options
557 */
558 public OpCodeBuilder(Writer w, Option... options) {
559 Function<CodeItem, String> namer = null;
560 boolean dropLocation = false;
561 boolean dropOpDescendants = false;
562 boolean writeVoidOpResult = false;
563 for (Option option : options) {
564 switch (option) {
565 case CodeItemNamerOption namerOption -> {
566 namer = namerOption.namer();
567 }
568 case LocationOption locationOption -> {
569 dropLocation = locationOption ==
570 LocationOption.DROP_LOCATION;
571 }
572 case OpDescendantsOption opDescendantsOption -> {
573 dropOpDescendants = opDescendantsOption ==
574 OpDescendantsOption.DROP_DESCENDANTS;
575 }
576 case VoidOpResultOption voidOpResultOption -> {
577 writeVoidOpResult = voidOpResultOption == VoidOpResultOption.WRITE_VOID;
578 }
579 }
580 }
581
582 this.w = new IndentWriter(w);
583 this.namer = (namer == null) ? new GlobalValueBlockNaming() : namer;
584 this.dropLocation = dropLocation;
585 this.dropOpDescendants = dropOpDescendants;
586 this.writeVoidOpResult = writeVoidOpResult;
587 }
588
589 /**
590 * {@return the function that names blocks and values.}
591 */
592 public Function<CodeItem, String> namer() {
593 return namer;
594 }
595
596 /**
597 * Writes a code model, an operation, to the character stream.
598 *
599 * @param op the code model
600 */
601 public OpCodeBuilder writeOp(Op op) {
602 if (op.parent() != null) {
603 Op.Result opr = op.result();
604 if (writeVoidOpResult || !opr.type().equals(JavaType.VOID)) {
605 writeValueDeclaration(opr).space().equal().space();
606 }
607 }
608 write(op.externalizeOpName());
609
610 if (!op.operands().isEmpty()) {
611 space().writeSpaceSeparatedList(op.operands(), this::writeValueUse);
612 }
613
614 if (!op.successors().isEmpty()) {
615 space().writeSpaceSeparatedList(op.successors(), this::writeSuccessor);
616 }
617
618 if (!dropLocation) {
619 Location location = op.location();
620 if (location != null) {
621 space().writeAttribute(ATTRIBUTE_LOCATION, op.location());
622 }
623 }
624 Map<String, Object> attributes = op.externalize();
625 if (!attributes.isEmpty()) {
626 space().writeSpaceSeparatedList(attributes.entrySet(), e -> writeAttribute(e.getKey(), e.getValue()));
627 }
628
629 if (!dropOpDescendants && !op.bodies().isEmpty()) {
630 int nBodies = op.bodies().size();
631 if (nBodies == 1) {
632 space();
633 } else {
634 nl().in().in();
635 }
636 boolean first = true;
637 for (Body body : op.bodies()) {
638 if (!first) {
639 nl();
640 }
641 writeBody(body);
642 first = false;
643 }
644 if (nBodies > 1) {
645 out().out();
646 }
647 }
648 semicolon();
649 return this;
650 }
651
652 OpCodeBuilder writeSuccessor(Block.Reference successor) {
653 writeBlockName(successor.targetBlock());
654 if (!successor.arguments().isEmpty()) {
655 oparen().nl().in().writeCommaSeparatedList(successor.arguments(), this::writeValueUse).out().nl().cparen();
656 }
657 return this;
658 }
659
660 OpCodeBuilder writeAttribute(String name, Object value) {
661 at();
662 if (!name.isEmpty()) {
663 write(name);
664 equal();
665 }
666 write(AttributeMapper.toString(value));
667 return this;
668 }
669
670 OpCodeBuilder writeBody(Body body) {
671 Block eb = body.entryBlock();
672 oparen();
673 if (!eb.parameters().isEmpty()) {
674 nl().in().writeCommaSeparatedList(eb.parameters(), this::writeValueDeclaration).out().nl();
675 }
676 cparen();
677 writeType(body.bodyType().returnType());
678 space().arrow().space();
679 obrace().nl().in();
680 for (Block b : body.blocks()) {
681 if (!b.isEntryBlock()) {
682 nl();
683 }
684 writeBlock(b);
685 }
686 out().cbrace();
687 return this;
688 }
689
690 OpCodeBuilder writeBlock(Block block) {
691 if (!block.isEntryBlock()) {
692 writeBlockName(block);
693 if (!block.parameters().isEmpty()) {
694 oparen().nl().in().writeCommaSeparatedList(block.parameters(), this::writeValueDeclaration).out().nl().cparen();
695 }
696 colon().nl();
697 }
698 in();
699 for (Op op : block.ops()) {
700 writeOp(op).nl();
701 }
702 out();
703 return this;
704 }
705
706 OpCodeBuilder writeBlockName(Block b) {
707 return hat().write(namer.apply(b));
708 }
709
710 OpCodeBuilder ssaid(Value v){
711 return percent().write(namer.apply(v));
712 }
713
714 OpCodeBuilder writeValueUse(Value v) {
715 return ssaid(v);
716 }
717
718 OpCodeBuilder writeValueDeclaration(Value v) {
719 return ssaid(v).space().colon().space().writeType(v.type());
720 }
721
722 <T> OpCodeBuilder writeSpaceSeparatedList(Iterable<T> l, Consumer<T> c) {
723 return writeSeparatedList(" ", l, c);
724 }
725
726 <T> OpCodeBuilder writeCommaSeparatedList(Iterable<T> l, Consumer<T> c) {
727 return writeSeparatedNlList(", ", l, c);
728 }
729
730 <T> OpCodeBuilder writeSeparatedList(String separator, Iterable<T> l, Consumer<T> c) {
731 boolean first = true;
732 for (T t : l) {
733 if (!first) {
734 write(separator);
735 }
736 c.accept(t);
737 first = false;
738 }
739 return this;
740 }
741 <T> OpCodeBuilder writeSeparatedNlList(String separator, Iterable<T> l, Consumer<T> c) {
742 boolean first = true;
743 for (T t : l) {
744 if (!first) {
745 write(separator);
746 nl();
747 }
748 c.accept(t);
749 first = false;
750 }
751 return this;
752 }
753 OpCodeBuilder writeType(TypeElement te) {
754 write(JavaTypeUtils.flatten(te.externalize()).toString());
755 return this;
756 }
757
758 OpCodeBuilder write(String s) {
759 w.write(s);
760 return this;
761 }
762 OpCodeBuilder nl() {
763 w.nl();
764 return this;
765 }
766
767 OpCodeBuilder semicolon(){
768 w.symbol(";");
769 return this;
770 }
771
772 OpCodeBuilder space(){
773 w.space();
774 return this;
775 }
776 OpCodeBuilder colon(){
777 w.symbol(":");
778 return this;
779 }
780 OpCodeBuilder oparen(){
781 w.symbol("(");
782 return this;
783 }
784 OpCodeBuilder obrace(){
785 w.symbol("{");
786 return this;
787 }
788 OpCodeBuilder cparen(){
789 w.symbol(")");
790 return this;
791 }
792 OpCodeBuilder cbrace(){
793 w.symbol("}");
794 return this;
795 }
796 OpCodeBuilder equal(){
797 w.symbol("=");
798 return this;
799 }
800 OpCodeBuilder in(){
801 w.in();
802 return this;
803 }
804 OpCodeBuilder out(){
805 w.out();
806 return this;
807 }
808 OpCodeBuilder arrow(){
809 w.symbol("->");
810 return this;
811 }
812 OpCodeBuilder at(){
813 w.symbol("@");
814 return this;
815 }
816 OpCodeBuilder hat(){
817 w.symbol("^");
818 return this;
819 }
820 OpCodeBuilder percent(){
821 w.symbol("%");
822 return this;
823 }
824 }